}else{
console.log('Commandnotrecognized');
}
Thenextthingyouneedtodoisaddanelseifclausethatchecks whetherthe
commandequalsremove.Intheelseif,I'llopenandclosemyconditionandhitenter
justasIdidinthepreviouselseifclause;thistime,I'lladdifthecommandequals
remove, we want to remove the note. And in that case, all we'll do is to use
console.logtoprintReadingnote,asshowninthefollowingcode:
console.log('Startingapp.js');
constfs=require('fs');
const_=require('lodash');
constnotes=require('./notes.js');
varcommand=process.argv[2];
console.log('Command:',command);
if(command==='add'){
console.log('Addingnewnote');
}elseif(command==='list'){
console.log('Listingallnotes');
}elseif(command==='read'){
console.log('Readingnote');
}else{
console.log('Commandnotrecognized');
}
Andwiththisinplace,wearedone.Ifwerefertothecodeblock,we'veadded
twonewcommandswecanrunoverintheTerminal,andwecantestthose:
if(command==='add'){
console.log('Addingnewnote');
}elseif(command==='list'){
console.log('Listingallnotes');
}elseif(command==='read'){
console.log('Readingnote');
}else{
console.log('Commandnotrecognized');
}
Firstup,I'llrunnodeapp.jswiththereadcommand,andReadingnoteshowsup:
console.log('Startingapp.js');
constfs=require('fs');
const_=require('lodash');
constnotes=require('./notes.js');
varcommand=process.argv[2];
console.log('Command:',command);
if(command==='add'){
console.log('Addingnewnote');
}elseif(command==='list'){
console.log('Listingallnotes');
}elseif(command==='read'){
console.log('Readingnote');
}elseif(command=='remove'){
console.log('Removingnote');
}else{
console.log('Commandnotrecognized');
}
ThenI'llrerunthecommand;thistime,I'llbeusingremove.AndwhenIdothat,
Removingnoteprintstothescreen,asshowninthisscreenshot:
I'llwrapupmytestingusingacommandthatdoesn'texist,andwhenIrunthat,
youcanseeCommandnotrecognizedshowsup.
Getting the specific note
information
Now,whatwedidintheprevioussubsectionisstep1.Wenowhavesupportfor
variouscommands.Thenextthingweneedtofigureoutishowwe'llgetmore
specificinformation.Forexample,whichnotedoyouwant toremove? Which
notedoyouwanttoread?Andwhatdoyouwantthenotetexttobeinthecase
ofaddinganote?ThisisallinformationweneedtogetfromtheTerminal.
Now,gettingitisgoingtobeprettysimilartowhatwedidearlier,andtoshow
you what it looks like, we'll print the entire argv object once again, using the
followingcommand:
console.log(process.argv);
OverintheTerminal,wecannowrunamorecomplexcommand.Let'ssaywe
wanttoremoveanoteusingthenodeapp.jsremovecommand,andwe'lldothatby
itstitle.Wemightusethetitleargument,whichlookslikethefollowingcode:
nodeapp.jsremove--title
In this title argument, we have -- (two) hyphens followed by the argument
name,whichistitle,followedbythe=(equals)sign.Thenwecantypeournote
title. Maybe the note title is secrets. This will pass the title argument into our
application.
Now,thereareacoupleofdifferentwaysyoucouldformatthetitleargument,
whichareasfollows:
Youcouldhavethetitlesecretsliketheoneintheprecedingcommand
You could have title equals secrets inside quotes, which will let us use
spacesinthetitle:
nodeapp.jsremove--title=secrets
Youcanremovethe=(equals)signaltogetherandsimplyputaspace:
nodeapp.jsremove--title="secrets2"
Nomatterhowyouchoosetoformatyourargument,theseareallvalidwaysto
passinthetitle.
Asyouseeintheprecedingscreenshot,I amusing doublequotes
when wrapping my string. Now, if you switch to single quotes, it
willnotbreakonLinuxorOSX,butitwillbreakonWindows.That
meanswhenyou'repassingincommand-lineargumentssuchasthe
titleorthenotebody,you'llwant towrap yourstrings, whenyou
have spaces, in double quotes, not single. So, if you are using
Windowsandyou'regettingsomesortofunexpectedbehaviorwith
your arguments, make sure you're using double quotes instead of
single;thatshouldfixtheissue.
For the moment, I'll keep the = (equals) sign and the quotes and rerun the
command:
nodeapp.jsremove--title="secrets2"
WhenIrunthecommand,youcanseeinthefollowingcodeoutputthatwehave
ourtwoarguments:
Thesearetheargumentsthatwedon'tneed,thenwehaveourremovecommand,
which is the third one, and we now have a new fourth string, the title that is
equal to secrets 2. And our argument was successfully passed into the
application.Theproblemisthatit'snotveryeasytouse.Inthefourthstring,we
havetoparseoutthekey,whichistitle,andthevalue,whichissecrets2.
When we used the command, which was the third argument in the previous
section,itwasaloteasiertouseinsideourapp.Wesimplypulleditoutofthe
arguments array and we referenced it by using the command variable and
checkingwhetheritequaledadd,list,read,orremove.
Things get a lot more complex as we use different styles for passing in the
arguments.Ifwe rerunthe lastcommandwithaspaceinsteadof an= (equals)
sign, as shown in the following code, which is perfectly valid, our arguments
arraynowlookscompletelydifferent:
In the preceding code output, you can see that we have the title as the fourth
item,and we have the value, which is secrets 2, as the fifth, which means we
have to add other conditions for parsing. And this turns into a pain really
quickly,whichiswhywewillnotdoit.
We'lluseathird-partymodulecalledyargsinthenextchaptertomakeparsing
thecommand-line arguments effortless. Instead of having strings, as shown in
this one or the one we discussed earlier, we'll get an object where the title
propertyequalsthesecrets2string.Thatwillmakeitsupereasytoimplementthe
restofthenotesapplication.
Now,parsingcertaintypesofcommand-linearguments,suchaskeyvaluepairs,
becomesalot morecomplex,whichis why,inthenextchapter,we'll beusing
yargstodojustthat.
Summary
Inthischapter,welearnedhowtouserequiretoloadinmodulesthatcomewith
Node.js.Wecreatedourfilesforournotesapplicationandrequiredtheminside
app.js. We explored how to use built-in modules and we explored how to use
modules we defined. We found out how to require other files that we created,
andhowtoexportthingssuchaspropertiesandfunctionsfromthosefiles.
Weexplorednpmalittlebit, howwecanusenpm inittogenerateapackage.json
file,andhowwecaninstallandusethird-partymodules.Next,weexploredthe
nodemonmodule,usingittoautomaticallyrestartourappaswemakechangestoa
file.Last,welearnedhowtogetinputfromtheuser,whichisneededtocreate
the notes application. We learned that we can use command-line arguments to
passdataintoourapp.
In the next chapter, we'll explore some more interesting Node fundamental
concepts,includingyargs,JSON,andRefactor.
NodeFundamentals–Part2
Inthischapter,we'llcontinueourdiscussiononsomemorenodefundamentals.
We'llexploreyargs,andwe'llseehowtoparsecommand-lineargumentsusing
process.argvandyargs.Afterthat,we'llexploreJSON.JSONisnothingmorethan
astringthatlookskindoflikeaJavaScriptobject,withthenotabledifferences
beingthatitusesdoublequotesinsteadofsinglequotesandallofyourproperty
names—likenameandage, in this case—require quotes around them. We'll look
into how to convert an object into a string, then define that string, use it, and
convertitbacktoanobject.
Afterwe'vedonethat,we'llfillouttheaddNotefunction.Finally,we'lllook into
refactor, moving the functionality into individual functions and testing the
functionality.
Morespecifically,we'llgothroughfollowingtopics:
yargs
JSON
Addingnote
Refactor
yargs
Inthissection,wewilluseyargs,athird-partynpmmodule,tomaketheprocess
of parsing much easier. It will let us access things such as title and body
informationwithoutneedingtowriteamanualparser.Thisisagreatexampleof
whenyoushouldlookforannpmmodule.Ifwedon'tuseamodule,itwouldbe
more productive for our Node application to use a third-party module that has
beentestedandthoroughlyvetted.
Togetstarted,we'llinstallthemodule,thenwe'lladditintotheproject,parsing
forthingssuchasatitleofthebody,andwe'llcallallthefunctionsthatwillget
definedoverinnotes.js.Ifthecommandisadd,we'llcalladdnote,soon.
Installingyargs
Now,let'sviewthedocumentspageforyargs.It'salwaysagoodideatoknow
whatyou'regettingyourselfinto.IfyousearchforyargsonGoogle,youshould
find the GitHub page as your first search result. As shown in the following
screenshot,wehavetheGitHubpagefortheyargslibrary:
Now,yargsisaverycomplexlibrary.Ithasatonoffeaturesforvalidatingall
sortsofinput,andithasdifferentwaysinwhichyoucanformatthatinput.We
will start with a very basic example, although we will be introducing more
complexexamplesthroughoutthischapter.
Ifyouwanttolookatanyotherfeaturesthatwedon'tdiscussinthe
chapter,oryoujustwanttoseehowsomethingworksthatwehave
talkedabout,youcanalwaysfinditintheyargdocuments.
We'llnowmoveintoTerminaltoinstallthismoduleinsideofourapplication.To
dothis,we'llusenpminstallfollowedbythemodulename,yargs,andinthiscase,
I'll use the @ sign to specify the specific version of the module I want to use,
11.0.0,whichisthemostrecentversionatthetimeofwriting.Next,I'lladdthe
saveflag,which,asweknow,updatesthepackage.jsonfile:
npminstallyargs@11.0.0--save
If I leave off the save flag, yargs will get installed into the
node_modules folder, but if we wipe that node_modules folder later and
runnpminstall,yargswon'tgetreinstalledbecauseit'snotlistedin
thepackage.jsonfile.Thisiswhyweusethesaveflag.
Runningyargs
Nowthat we've installed yargs,wecanmove over into Atom,insideof app.js,
andgetstartedwithusingit.Thebasicsofyargs,theverycoreofitsfeatureset,
isreallysimpletotakeadvantageof.Thefirstthingwe'lldoistorequireitup,as
wedidwithfsandlodashinthepreviouschapter.Let'smakeaconstantandcallit
yargs,settingitequaltorequire('yargs'),asshownhere:
console.log('Startingapp.js');
constfs=require('fs');
const_=require('lodash');
constyargs=require('yargs');
constnotes=require('./notes.js');
varcommand=process.argv[2];
console.log('Command:',command);
console.log(process.argv);
if(command==='add'){
console.log('Addingnewnote');
}elseif(command==='list'){
console.log('Listingallnotes');
}elseif(command==='read'){
console.log('Readingnote');
}elseif(command==='remove'){
console.log('Removingnote');
}else{
console.log('Commandnotrecognized');
}
From here, we can fetch the arguments as yargs parses them. It will take the
same process.argv array that we discussed in the previous chapter, but it goes
behind the scenes and parses it, giving us something that's much more useful
than what Node gives us. Just above the command variable, we can make a const
variablecalledargv,settingitequaltoyargs.argv,asshownhere:
console.log('Startingapp.js');
constfs=require('fs');
const_=require('lodash');
constyargs=require('yargs');
constnotes=require('./notes.js');
constargv=yargs.argv;
varcommand=process.argv[2];
console.log('Command:',command);
console.log(process.argv);
if(command==='add'){
console.log('Addingnewnote');
}elseif(command==='list'){
console.log('Listingallnotes');
}elseif(command==='read'){
console.log('Readingnote');
}elseif(command==='remove'){
console.log('Removingnote');
}else{
console.log('Commandnotrecognized');
}
The yargs.argv module is where the yargs library stores its version of the
argumentsthatyourappranwith.Nowwecanprintitusingconsole.log,andthis
will let us take a look at the process.argv and yargs.argv variables; we can also
compare them and see how yargs differs. For the command where we use
console.logtoprintprocess.argv,I'llmakethefirstargumentastringcalledProcess
sothatwecandifferentiateitinTerminal.We'llcallconsole.logagain.Thefirst
argument will be the Yargs string, and the second one will be the actual argv
variable,whichcomesfromyargs:
console.log('Startingapp.js');
constfs=require('fs');
const_=require('lodash');
constyargs=require('yargs');
constnotes=require('./notes.js');
constargv=yargs.argv;
varcommand=process.argv[2];
console.log('Command:',command);
console.log('Process',process.argv);
console.log('Yargs',argv);
if(command==='add'){
console.log('Addingnewnote');
}elseif(command==='list'){
console.log('Listingallnotes');
}elseif(command==='read'){
console.log('Readingnote');
}elseif(command==='remove'){
console.log('Removingnote');
}else{
console.log('Commandnotrecognized');
}
Nowwecanrunourapp(refertotheprecedingcodeblock)afewdifferentways
andseehowthesetwoconsole.logstatementsdiffer.
Firstup,we'llrunatnodeapp.jswiththeaddcommand,andwecanrunthisvery
basicexample:
nodeapp.jsadd
Wealreadyknowwhattheprocess.argvarraylookslikefromthepreviouschapter.
Theusefulinformationisthethirdstringinsideofthearray,whichis'add'.Inthe
fourthstring,Yargsgivesusanobjectthatlooksverydifferent:
Asshown in thepreceding code output,first wehavethe underscoreproperty,
thencommandssuchasaddarestored.
IfIweretoaddanothercommand,sayadd,andthenIweretoaddamodifier,say
encrypted,youwouldseethataddwouldbethefirstargumentandencryptedthe
second,asshownhere:
nodeapp.jsaddencrypted
Sofar,yargsreallyisn'tshining.Thisisn'tmuchmoreusefulthanwhatwehave
inthepreviousexample.Whereitreallyshinesiswhenwestartpassinginkey-
value pairs, such as the title example we used in the Getting input section of
NodeFundamentals-Part1inchapter2.Icansetmytitleflagequaltosecrets,
pressenter,andthistimearound,wegetsomethingmuchmoreuseful:
nodeapp.jsadd--title=secrets
In the following code output, we have the third string that we would need to
parseinordertofetchthevalueandthekey,andinthefourthstring,weactually
haveatitlepropertywithavalueofsecrets:
Also,yargshasbuilt-inparsingforallthedifferentwaysyoucouldspecifythis.
Wecaninsertaspaceaftertitle,anditwillstillworkjustasitdidbefore;we
canaddquotesaroundsecrets,oraddotherwords,likesecretsfromAndrew,andit
will still parses it correctly, setting the title property to the secrets from Andrew
string,asshownhere:
nodeapp.jsadd--title"secretsfromAndrew"
This is where yargs really shines! It makes the process of parsing your
argumentsaloteasier.Thismeansthatinsideourapp,wecantakeadvantageof
thatparsingandcalltheproperfunctions.
Workingwiththeaddcommand
Let'sworkwiththeaddcommand,forexample,forparsingyourargumentsand
calling the functions. Once the add command gets called, we want to call a
functiondefinedinnotes,whichwillberesponsibleforactuallyaddingthenote.
Thenotes.addNotefunctionwillgetthejobdone.Now,whatdowewanttopassto
theaddNotefunction?Wewanttopassintwothings:thetitle,whichisaccessible
onargv.title,aswesawintheprecedingexample;andthebody,argv.body:
console.log('Startingapp.js');
constfs=require('fs');
const_=require('lodash');
constyargs=require('yargs');
constnotes=require('./notes.js');
constargv=yargs.argv;
varcommand=process.argv[2];
console.log('Command:',command);
console.log('Process',process.argv);
console.log('Yargs',argv);
if(command==='add'){
console.log('Addingnewnote');
notes.addNote(argv.title,argv.body);
}elseif(command==='list'){
console.log('Listingallnotes');
}elseif(command==='read'){
console.log('Readingnote');
}elseif(command==='remove'){
console.log('Removingnote');
}else{
console.log('Commandnotrecognized');
}
Currently, these command-line arguments, title and body, aren't
required.Sotechnically,theusercouldruntheapplicationwithout
oneofthem,whichwouldcauseittocrash,butinfuture,we'llbe
requiringbothofthese.
Nowthatwehavenotes.addNoteinplace,wecanremoveourconsole.logstatement,
which was just a placeholder, and we can move into the notes application
notes.js.
Insidenotes.js,we'llgetstartedbymakingavariablewiththesamenameasthe
methodweusedoverapp.jsandaddNote,andwewillsetitequaltoananonymous
arrowfunction,asshownhere:
varaddNote=()=>{
};
Now,thisaloneisn'ttoouseful,becausewe'renotexportingtheaddNotefunction.
Below the variable, we can define module.exports in a slightly different way. In
previous sections, we added properties onto exports to export them. We can
actuallydefineanentireobjectthatgetssettoexports,andinthiscase,wecanset
addNoteequaltotheaddNotefunctiondefinedinprecedingcodeblock:
module.exports={
addNote:addNote
};
InES6,there'sactuallyashortcutforthis.Whenyou'resettingan
object attribute and a value that's a variable and they're both
exactly the same, you can actually leave off the colon and the
value.Eitherway,theresultidentical.
In the preceding code, we're setting an object equal to module.exports, and that
objecthasaproperty,addNote,whichpointstotheaddNotefunctionwedefinedasa
variableintheprecedingcodeblock.
Onceagain,addNote:andaddNoteareidenticalinsideofES6.Wewillbeusingthe
ES6syntaxforeverythingthroughoutthisbook.
NowIcantakemytwoarguments,titleandbody,andactuallydosomethingwith
them. In this case, we'll call console.log and Adding note, passing in the two
arguments as the second and third argument to console.log, title and body, as
shownhere:
varaddNote=(title,body)=>{
console.log('Addingnote',title,body);
};
Nowwe'reinaprettygoodpositiontoruntheaddcommandwithtitleandbody
and see if we get exactly what we'd expect, which is the console.log statement
shownintheprecedingcodetoprint.
Over in Terminal, we can start by running the app with node app.js, and then
specifythefilename.We'llusetheaddcommand;whichwillruntheappropriate
function.Then,we'llpassintitle,settingitequaltosecret,andthenwecanpass
inbody,whichwillbeoursecondcommand-lineargument,settingthatequalto
thestring,Thisismysecret:
nodeapp.jsadd--title=secret--body="Thisismysecret"
Inthiscommand,wespecifiedthreethings:theaddcommandthetitleargument,
which gets set to secret; and the body argument, which gets set to "This is my
secret".Ifallgoeswell,we'llgettheappropriatelog.Let'srunthecommand.
Inthefollowingcommandoutput,youcanseeAddingnotesecret,whichisthe
title;andThisismysecret,whichisthebody:
Withthisinplace,wenowhaveoneofourmethodssetupandreadytogo.The
nextthingthatwe'lldoisconverttheothercommandswehave—thelist,read,
andremovecommands.Let'slookintoonemorecommand,andthenyou'lldothe
othertwobyyourselfasexercises.
Workingwiththelistcommand
Now, with the list command, I'll remove the console.log statement and call
notes.getAll,asshownhere:
console.log('Startingapp.js');
constfs=require('fs');
const_=require('lodash');
constyargs=require('yargs');
constnotes=require('./notes.js');
constargv=yargs.argv;
varcommand=process.argv[2];
console.log('Command:',command);
console.log('Process',process.argv);
console.log('Yargs',argv);
if(command==='add'){
notes.addNote(argv.title,argv.body);
}elseif(command==='list'){
notes.getAll();
}elseif(command==='read'){
console.log('Readingnote');
}elseif(command==='remove'){
console.log('Removingnote');
}else{
console.log('Commandnotrecognized');
}
Atsomepoint,notes.getAll will returnallof the notes. Now,getAlldoesn'ttake
anyargumentssinceitwillreturnallofthenotesregardlessofthetitle.Theread
commandwillrequireatitle,andremovewillalsorequirethetitleofthenoteyou
wanttoremove.
Fornow,wecancreatethegetAllfunction.Insidenotes.js,we'llgothroughthat
process again. We'll start by making a variable, calling it getAll, and setting it
equal to an arrow function, which we've used before. We start with our
argumentslist, then we set up the arrow (=>), which is the equal sign and the
greater than sign. Next, we specify the statements we want to run. Inside our
codeblock,we'llrunconsole.log(Gettingallnotes),asshownhere:
vargetAll=()=>{
console.log('Gettingallnotes');
};
ThelaststeptotheprocessafteraddingthatsemicolonwillbetoaddgetAllto
theexports,asshowninthefollowingcodeblock:
module.exports={
addNote,
getAll
};
Remember that in ES6, if you have a property whose name is
identicaltothevalue,whichisavariable,youcansimplyremove
thevaluevariableandthecolon.
NowthatwehavegetAllinnotes.jsinplace,andwe'vewireditupinapp.js,we
canrunthingsoverinTerminal.Inthiscase,we'llrunthelistcommand:
nodeapp.jslist
In the preceding code output, you can see at the bottom that Getting all notes
prints to the screen. Now that we have this in place, we can remove
console.log('Process',process.argv)fromthecommandvariableinapp.js.Theresultant
codewilllooklikethefollowingcodeblock:
console.log('Startingapp.js');
constfs=require('fs');
const_=require('lodash');
constyargs=require('yargs');
constnotes=require('./notes.js');
constargv=yargs.argv;
varcommand=process.argv[2];
console.log('Command:',command);
console.log('Yargs',argv);
if(command==='add'){
notes.addNote(argv.title,argv.body);
}elseif(command==='list'){
notes.getAll();
}elseif(command==='read'){
console.log('Readingnote');
}elseif(command==='remove'){
console.log('Removingnote');
}else{
console.log('Commandnotrecognized');
}
Wewillkeeptheyargslogaroundsincewe'llbeexploringtheotherwaysand
methodstouseyargsthroughoutthechapter.
Now that we have the list command in place, next, I'd like you to create a
methodforthereadandremovecommands.
Thereadcommand
Whentheread command is used,wewant to call notes.getNote,passingintitle.
Now,titlewillgetpassedinandparsedusingyargs,whichmeansthatwecan
useargv.titletofetchit.Andthat'sallwehavetodowhenitcomestocallingthe
function:
console.log('Startingapp.js');
constfs=require('fs');
const_=require('lodash');
constyargs=require('yargs');
constnotes=require('./notes.js');
constargv=yargs.argv;
varcommand=process.argv[2];
console.log('Command:',command);
console.log('Yargs',argv);
if(command==='add'){
notes.addNote(argv.title,argv.body);
}elseif(command==='list'){
notes.getAll();
}elseif(command==='read'){
notes.getNote(argv.title);
}elseif(command==='remove'){
console.log('Removingnote');
}else{
console.log('Commandnotrecognized');
}
The next step is to define getNote, because currently it doesn't exist. Over in
notes.js, right below the getAll variable, we can make a variable called getNote,
which will be a function. We'll use the arrow function, and it will take an
argument;it will take the note title. The getNote function takes the title, then it
returnsthebodyforthatnote:
vargetNote=(title)=>{
};
InsidegetNote,wecanuseconsole.logtoprintsomethinglikeGettingnote,followed
by the title of the note you will fetch, which will be the second argument to
console.log:
vargetNote=(title)=>{
console.log('Gettingnote',title);
};
Thisisthefirstcommand,andwecannowtestitbeforewegoontothesecond
one,whichisremove.
OverinTerminal,wecanusenodeapp.jstorunthefile.We'llbeusingthenew
readcommand,passinginatitleflag.I'lluseadifferentsyntax,wheretitlegets
setequaltothevalueoutsideofquotes.I'llusesomethinglikeaccounts:
nodeapp.jsread--titleaccounts
Thisaccountsvaluewillreadtheaccountsnoteinthefuture,anditwillprintitto
thescreen,asshownhere:
Asyoucanseeintheprecedingcodeoutput,wegetanerror,whichwe'lldebug
now.
Dealingwiththeerrorsinparsing
commands
Gettinganerrorisnottheendoftheworld.Gettinganerrorusuallymeansthat
youhaveasmalltypooryouforgotonestepintheprocess.So,we'llfirstfigure
outhowtoparsethroughtheseerrormessages,becausetheerrormessagesyou
getinthecodeoutputcanbeprettydaunting.Let'srefertothecodeoutputerror
here:
Asyoucansee,thefirstlineshowsyouwheretheerroroccurred.It'sinsideof
ourapp.jsfile,andthenumber19afterthecolonisthelinenumber.Itshowsyou
exactlywherethingswentbad.TheTypeError:notes.getNoteisnotafunctionlineis
tellingyouprettyclearlythatthegetNotefunctionyoutriedtorundoesn'texist.
Nowwecantakethisinformationanddebugourapp.
Inapp.js,weseethatwecallnotes.getNote.Everythinglooksgreat,butwhenwe
moveintonotes.js,werealizethatweneveractuallyexportedgetNote.Thisiswhy
whenwetrytocallthefunction,wegetgetNoteis not afunction.Allwehaveto
dotofixthaterrormessageisexportgetNote,asshownhere:
module.exports={
addNote,
getAll,
getNote
};
NowwhenwesavethefileandreruntheappfromTerminal,we'llgetwhatwe
expect—Gettingnotefollowedbythetitle,whichisaccounts,asshownhere:
This is how we can debug our error messages. Error messages contain really
useful information. For the most part, the first couple of lines are code that
you'vewritten,andtheotheronesareinternalNodecodeorthird-partymodules.
In our case, the first line of the stack trace is important, as it shows exactly
wheretheerroroccurred.
Theremovecommand
Now,sincethereadcommandisworking,wecanmoveontothelastone,which
istheremovecommand.Here,I'llcallnotes.removeNote,passinginthetitle,whichas
weknowisavailableinargv.title:
console.log('Startingapp.js');
constfs=require('fs');
const_=require('lodash');
constyargs=require('yargs');
constnotes=require('./notes.js');
constargv=yargs.argv;
varcommand=process.argv[2];
console.log('Command:',command);
console.log('Yargs',argv);
if(command==='add'){
notes.addNote(argv.title,argv.body);
}elseif(command==='list'){
notes.getAll();
}elseif(command==='read'){
notes.getNote(argv.title);
}elseif(command==='remove'){
notes.removeNote(argv.title);
}else{
console.log('Commandnotrecognized');
}
Next up, we'll define the removeNote function over inside of our notes API file,
rightbelowthegetNotevariable:
varremoveNote=(title)=>{
console.log('Removingnote',title);
};
Now,removeNotewillworkmuchthesamewayasgetNote.Allitneedsisthetitle;it
canusethisinformationtofindthenoteandremoveitfromthedatabase.This
willbeanarrowfunctionthattakesthetitleargument.
Inthiscase,we'llprinttheconsole.logstatement,Removingnote;then,asthesecond
argument,we'llsimplyprinttitlebacktothescreentomakesurethatit'sgoing
through the process successfully. This time around, we'll export our removeNote
function;we'lldefineitusingtheES6syntax:
module.exports={
addNote,
getAll,
getNote,
removeNote
};
The last thing to do is test it and make sure it works. We can reload the last
commandusing the up arrow key. We change read to remove, and that is all we
needtodo.We'restillpassinginthetitleargument,whichisgreat,becausethat
iswhatremoveneeds:
nodeapp.jsremove--titleaccounts
When I run this command, we get exactly what we expected. Removing note
printstothescreen,asshowninthefollowingcodeoutput,andthenwegetthe
titleofthenotethatwe'resupposedtoberemoving,whichisaccounts:
Thislooksgreat!Thatisallittakestouseyargstoparseyourarguments.
With this, we now have a place to define all of that functionality, for saving,
reading,listing,andremovingnotes.
Fetchingcommand
ThelastthingIwanttodiscussbeforewewrapupthissectionis—howwefetch
command.
Asweknow,commandisavailableinthe_propertyasthefirstandonlyitem.This
means that in the app.js,var command statement, we can set command equal to argv,
then._,andthenwe'lluse[]tograbthefirstiteminthearray,asshowninthe
followingcode:
console.log('Startingapp.js');
constfs=require('fs');
const_=require('lodash');
constyargs=require('yargs');
constnotes=require('./notes.js');
constargv=yargs.argv;
varcommand=argv._[0];
console.log('Command:',command);
console.log('Yargs',argv);
if(command==='add'){
notes.addNote(argv.title,argv.body);
}elseif(command==='list'){
notes.getAll();
}elseif(command==='read'){
notes.getNote(argv.title);
}elseif(command==='remove'){
notes.removeNote(argv.title);
}else{
console.log('Commandnotrecognized');
}
With this in place, we now have the same functionality, but we'll use yargs
everywhere.IfIrerunthelastcommand,wecantestthatthefunctionalitystill
works.Anditdoes!Asshowninthefollowingcommandoutput,wecanseethat
Command:removeshowsup:
Next,we'lllookintofillingouttheindividualfunctions.We'lltakealookfirstat
howwecanuseJSONtostoreournotesinsideourfilesystem.
JSON
Nowthatyouknowhowtoparsecommand-lineargumentsusingprocess.argvand
yargs,you'vesolvedthefirstpiecetothepuzzleforthenotesapplication.Now,
howdowegetthatuniqueinputfromtheuser?Thesecondpiecetothepuzzleis
tosolvehowwestorethisinformation.
Whensomeoneaddsanewnote,wewanttosaveitsomewhere,preferablyon
thefilesystem.Sothenexttimetheytrytofetch,remove,orreadthatnote,they
actuallygetthenoteback.Todothis,we'llneedtointroducesomethingcalled
JSON. If you're already familiar with JSON, you probably know it is super
popular. Itstandsfor JavaScriptObject Notation,and it's a way to represent
JavaScriptarraysandobjectsusingastring.Now,whywouldyoueverwantto
dothat?
Well, you might want to do that because strings are just text, and that's pretty
muchsupportedanywhere.IcansaveJSONtoatextfile,andthenIcanreadit
later, parseit backintoa JavaScriptarray or object,and dosomethingwith it.
Thisisexactlywhatwe'lltakealookatinthissection.
ToexploreJSONandhowitworks,let'sgoaheadandmakeanewfolderinside
ourprojectcalledplayground.
Throughout the book, I'll create the playground folders and various
projects, which store simple one-off files that aren't a part of the
biggerapplication;they'rejustawaytoexplore anewfeatureor
learnanewconcept.
In the playground folder, we'll make a file called json.js, this is where we can
explorehowJSONworks.Togetstarted,let'smakeaverysimpleobject.
Convertingobjectsintostrings
Let'sfirstmakeavariablecalledobj,settingitequaltoanobject.Onthisobject,
we'lljustdefineoneproperty,name,andsetitequaltoyourfirstname;I'llsetthis
oneequaltoAndrew,asshownhere:
varobj={
name:'Andrew'
};
Now,let'sassumethatwewanttotakethisobjectandworkonit.Let'ssaywe
wantto,forexample,senditbetweenserversasastringandsaveittoatextfile.
Todothis,we'llneedtocalloneJSONmethod.
Let'stakeamomenttodefineavariabletostoretheresult,stringObj,andwe'llset
itequaltoJSON.stringify,asshownhere:
varstringObj=JSON.stringify(obj);
The JSON.stringify method takes your object, in this case, the obj variable, and
returnstheJSON-stringifiedversion.ThismeansthattheresultstoredinstringObj
isactuallyastring.It'snolongeranobject,andwecantakealookatthatusing
console.log.I'lluseconsole.logtwice.Firstup,we'llusethetypeofoperatortoprint
thetypeofthestringobjecttomakesurethatitactuallyisastring.Sincetypeofis
anoperator,itgetstypedinlowercase,thereisnocamelcasing.Then,youpass
inthevariablewhosetypeyouwanttocheck.Nextup,wecanuseconsole.logto
printthecontentsofthestringitself,printingoutthestringObjvariable,asshown
here:
console.log(typeofstringObj);
console.log(stringObj);
Whatwe'vedonehereiswe'vetakenanobject,converteditintoaJSONstring,
andprinteditontothescreen.OverinTerminal,I'llnavigateintotheplayground
folderusingthefollowingcommand:
cdplayground
For now, it doesn't matter where you run the command, but in
futureitwillmatterwhenweareintheplaygroundfolder,sotakea
momenttonavigateintoit.
Wecannowuse nodeto run our json.js file.When we run the file,we see two
things:
Asshowninthe preceding code output, first, we willgetour type, which is a
string,andthisisgreat,becauseremember,JSONisastring.Next,wewillget
ourobject,whichlooksprettysimilartoaJavaScriptobject,butthereareafew
differences.Thesedifferencesareasfollows:
Firstup,yourJSONwillhaveitsattributenamesautomaticallywrappedin
doublequotes.ThisisarequirementoftheJSONsyntax.
Next up, you'll notice your strings are also wrapped in double quotes as
opposedtosinglequotes.
Now,JSONdoesn'tjustsupportstringvalues,youcanuseanarray,aBoolean,a
number,or anything else. All of those types are perfectly valid inside of your
JSON. In this case, we have a very simple example where we have a name
propertyandit'ssetto"Andrew".
Thisistheprocessoftakinganobjectandconvertingitintoastring.Nextup,
we'lldefinea stringand convertthatintoanobject wecan actuallyusein our
app.
Definingastringandusinginapp
asanobject
Let'sgetstartedbymakingavariablecalledpersonString,andwe'lltosetitequal
toastringusingsinglequotessinceJSONusesdoublequotesinsideofitself,as
shownhere:
varpersonString='';
Then we'll define our JSON in the quotes. We'll start by opening and closing
somecurly braces. We'll use double quotes to create our first attribute, which
we'llcallname,andwe'llsetthatattributeequaltoAndrew.Thismeansthatafterthe
closing quote, we'll add :; then we'll open and close double quotes again and
typethevalueAndrew,asshownhere:
varpersonString='{"name":"Andrew"}';
Nextup,wecanaddanotherproperty.Afterthevalue,Andrew,I'llcreateanother
propertyafterthecomma,calledage,whichwillbesetequaltoanumber.Ican
usemycolonandthendefinethenumberwithoutthequotes,inthiscase,25:
varpersonString='{"name":"Andrew","age":25}';
Youcangoaheadanduseyournameandyourage,obviously,butmakesurethe
restlooksidenticaltowhatyouseehere.
Now,let'ssaywegettheearlier-definedJSONfromaserverorwegrabitfroma
textfile.Currently,it'suseless;ifwewanttogetthenamevalue,thereisnogood
way to do that because we're using a string, so personString.name doesn't exist.
Whatweneedtodoistakethestringandconvertitbackintoanobject.
Converting a string back to an
object
To convert the string back to object, we'll use the opposite of JSON.stringify,
which is JSON.parse. Let's make a variable to store the result. I'll create a person
variable and it will be set equal to JSON.parse, passing in as the one and only
argumentthestringyou wanttoparse,inthiscase,thepersonstring, which we
definedearlier:
varperson=JSON.parse(personString);
Now, thisvariabletakes yourJSON andconverts itfroma stringback intoits
originalform, which couldbeanarrayoran object. In our case, it converts it
backintoanobject,andwehavethepersonvariableasanobject,asshowninthe
precedingcode.Also,wecanprovethatit'sanobjectusingthetypeofoperator.
I'lluseconsole.logtwice,justlikewedidpreviously.
Firstup, we'll print typeof person, and then we'll print the actual personvariable,
console.log(person):
console.log(typeofperson);
console.log(person);
Withthisinplace,wecannowrerunthecommandinTerminal;I'llactuallystart
nodemonandpassinjson.js:
nodemonjson.js
Asshowninthefollowingcodeoutput,youcannowseethatwe'reworkingwith
anobject,whichisgreat,andwehaveourregularobject:
WeknowthatAndrewisanobjectbecauseit'snotwrappedindoublequotes;the
valuesdon'thaveanyquotes,andweusesinglequotesforAndrew,whichisvalid
inJavaScript,butit'snotvalidinJSON.
Thisistheentireprocessoftakinganobject,convertingittoastring,andthen
takingthestringandconvertingitbackintotheobject,andthisisexactlywhat
we'lldointhenotesapp.Theonlydifferenceisthatwe'llbetakingthefollowing
stringandstoringitinafile,thenlateron,we'llbereadingthatstringfromthe
file using JSON.parse to convert it back to an object, as shown in the following
codeblock:
//varobj={
//name:'Andrew'
//};
//varstringObj=JSON.stringify(obj);
//console.log(typeofstringObj);
//console.log(stringObj);
varpersonString='{"name":"Andrew","age":25}';
varperson=JSON.parse{personString};
console.log(typeofperson);
console.log(person);
Storingthestringinafile
Withthebasicsinplace,let'stakeitjustonestepfurther,thatis,bystoringthe
stringinafile.Then,wewanttoreadthecontentsofthatfilebackbyusingthe
fs module and printing some properties from it. This means that we'll need to
convert the string that we get back from fs.readfilesync into an object using
JSON.parse.
Writing the file in the playground
folder
Let's go ahead and comment out all the code we have so far and start with a
cleanslate.Firstup,let'sgoaheadandloadinthefsmodule.Theconstvariable
fswillbesetequaltorequire,andwe'llpassthefsmodulethatwe'veusedinthe
past,asshownhere:
//varobj={
//name:'Andrew'
//};
//varstringObj=JSON.stringify(obj);
//console.log(typeofstringObj);
//console.log(stringObj);
//varpersonString='{"name":"Andrew","age":25}';
//varperson=JSON.parse(personString);
//console.log(typeofperson);
//console.log(person);
constfs=require('fs');
Thenextthingwe'lldoisdefinetheobject.Thisobjectwillbestoredinsideof
our file, and then will be read back and parsed. This object will be a variable
calledoriginalNote,andwe'llcallitoriginalNotebecauselateron,we'llloaditback
inandcallthatvariableNote.
Now,originalNote will be a regular JavaScript object with two properties. We'll
havethetitleproperty,whichwe'llsetequaltoSometitle,andthebodyproperty,
whichwewillsetequaltoSomebody,asshownhere:
varoriginalNote={
title:'Sometitle',
body:'Somebody'
};
The next step that you will need to do is take the original note and create a
variablecalledoriginalNoteString,andsetthatvariableequaltotheJSONvalueof
theobjectwedefinedearlier.Thismeansthatyou'llneedtouseoneofthetwo
JSONmethodsweusedpreviouslyinthissection.
Now, once you have that originalNoteString variable, we can write a file to the
filesystem.I'll writethat linefor you, fs.writeFileSync. The writeFileSyncmethod,
whichweusedbefore,takestwoarguments.Onewillbethefilename,andsince
we'reusingJSON,it'simportanttousetheJSONfileextension.I'llcallthisfile
notes.json. The other arguments will be text content, originalNoteString,which is
notyetdefined,asshowninthiscodeblock:
//originalNoteString
fs.writeFileSync('notes.json',originalNoteString);
This is the first step to the process; this is how we'll write that file into the
playground folder. The next step to the process will be to read out the contents,
parse it using the JSON method earlier, and print one of the properties to the
screentomakesurethatit'sanobject.Inthiscase,we'llprintthetitle.
Readingoutthecontentinthefile
Thefirststeptoprintthetitleistouseamethodwehaven'tusedyet.We'lluse
theread method available on the filesystem module to read the contents. Let's
make a variable called noteString. The noteString variable will be set equal to
fs.readFileSync.
Now, readFileSync is similar to writeFileSync except that it doesn't take the text
content, since it's getting the text content back for you. In this case, we'll just
specifythefirstargument,whichisthefilename,notes.JSON:
varnoteString=fs.readFileSync('notes.json');
Nowthatwehavethestring,itwillbeyourjobtotakethatstring,useoneofthe
precedingmethods,andconvertitbackintoanobject.Youcancallthatvariable
note.Nextup,theonlythinglefttodoistotestwhetherthingsareworkingas
expected,byprintingwith thehelpofconsole.log(typeof note). Then, below this,
we'lluseconsole.logtoprintthetitle,note.title:
//note
console.log(typeofnote);
console.log(note.title);
Now, over in Terminal, you can see (refer to the following screenshot) that I
havesaved the file in a broken state and it crashed, and that's expected when
you'reusingnodemon:
To resolve this, the first thing I'll do is fill out the originalNoteString variable,
which we had commented out earlier. It will now be a variable called
originalNoteString,andwe'llsetitequaltothereturnvaluefromJSON.stringify.
Now,weknowJSON.stringifytakesourregularobjectanditconvertstheobject
intoastring.Inthiscase,we'lltaketheoriginalNoteobjectandconvert itintoa
string. The next line, which we already have filled out, will save that JSON
valueintothenotes.JSONfile.Thenwewillreadthatvalueout:
varoriginalNoteString=JSON.stringify(originalNote);
The next step will be to create the note variable. The note variable will be set
equaltoJSON.parse.
TheJSON.parsemethodtakesthestringJSONandconvertsitbackintoaregular
JavaScriptobjectorarray,dependingonwhateveryousave.Herewewillpassin
noteString,whichwe'llgetfromthefile:
varnote=JSON.parse(noteString);
With this in place, we are now done. When I save this file, nodemon will
automaticallyrestartandwewouldexpecttonotseeanerror.Instead,weexpect
thatwe'llseetheobjecttypeaswellasthenotetitle.RightinsideTerminal,we
haveobjectandSometitleprintingtothescreen:
Withthis inplace,we've successfullycompletedthe challenge.This is exactly
howwewillsaveournotes.
Whensomeoneaddsanewnote,we'llusethefollowingcodetosaveit:
varoriginalNote={
title:'Sometitle',
body:'Somebody'
};
varoriginalNoteString=JSON.stringify(originalNote);
fs.writeFileSync('notes.json',originalNoteString);
Whensomeonewantstoreadtheirnote,we'llusethefollowingcodetoreadit:
varnoteString=fs.readFileSync('notes.json');
varnote=JSON.parse(noteString);
console.log(typeofnote);
console.log(note.title);
Now,whatifsomeonewantstoaddanote?Thiswillrequireustofirstreadall
of the notes, then modify the notes array, and then use the code (refer to the
previouscodeblock)tosavethenewarraybackintothefilesystem.
Ifyouopenupthatnotes.JSONfile,youcanseerightherethatwehaveourJSON
codeinsidethefile:
.jsonisactuallyafileformatthat'ssupportedbymosttexteditors,soIactually
alreadyhave some nice syntax highlighting built in. Now, in the next section,
we'll be filling outtheaddNote function using the exact same logic that we just
usedinsideofthissection.
Addingandsavingnotes
Intheprevioussection,youlearnedhowtoworkwithJSONinsideNode.js,and
thisistheexactformatwe'llbeusingforthenotes.jsapplication.Whenyoufirst
runacommand,we'llloadinallthe notesthatmightalready exist.Thenwe'll
run the command, whether it's adding, removing, or reading notes. Finally, if
we'veupdatedthearray,likewewillwhenweaddandremovenotes,we'llsave
thosenewnotesbackintotheJSONfile.
Now,thiswillallhappeninsideoftheaddNotefunction,whichwedefinedinthe
notes.jsapplication,andwealreadywiredupthisfunction.Inearliersections,we
ran the app add command, and this function executed with the title and body
arguments.
Addingnotes
Togetstartedwithaddingnotes,thefirstthingwe'lldoiscreateavariablecalled
notes, and for the moment, we'll set it equal to an empty array, just as in the
following,usingoursquarebrackets:
varaddNote=(title,body)=>{
varnotes=[];
};
Nowthatwehavetheemptyarray,wecangoaheadandmakeavariablecalled
note,whichistheindividualnote.Thiswillrepresentthenewnote:
varaddNote=(title,body)=>{
varnotes=[];
varnote={

}
};
Onthatnote,we'llhavethetwoproperties:atitleandabody.Now,titlecanbe
set equal to the title variable, but, as we know, inside ES6, we can simply
removeitwhenboth valuesarethesame;sowe'lladdtitleandbody as shown
here:
varaddNote=(title,body)=>{
varnotes=[];
varnote={
title,
body
};
};
Nowwehavethenoteandthenotesarray.
Addingnotestothenotesarray
Thenextstepintheprocessofaddingnoteswillbetoaddthenotetothenotes
array.Thenotes.pushmethodwillletusdojustthat.Thepushmethodonanarray
lets you pass in an item, which gets added to the end of the array, and in this
case,we'llpassin thenoteobject.So wehaveanemptyarray,andweaddour
oneitem,asshowninthefollowingcode;next,wepushitin,whichmeansthat
wehaveanarraywithoneitem:
varaddNote=(title,body)=>{
varnotes=[];
varnote={
title,
body
};
notes.push(note);
};
Thenextstepintheprocesswillbetoupdatethefile.Now,wedon'thaveafile
inplace,butwecanloadanfsfunctionandstartcreatingthefile.
Up above the addNote function, let's load in the fs module. I'll create a const
variable called fs and set it equal to the return result from require, and we'll
requirethefsmodule,whichisacorenodemodule,sothere'snoneedtoinstall
itusingNPM:
constfs=require('fs');
Withthisinplace,wecantakeadvantageoffsinsidetheaddNotefunction.
Right after we push our item on to the notes array, we'll call fs.writeFileSync,
whichwe'veusedbefore.Weknowweneedtopassintwothings:thefilename
andthe content we want to save. For the file, I'll call, notes-data.JSON, and then
we'll pass in the content to save, which in this case will be the stringifynotes
array,whichmeanswecancallJSON.stringifypassinginnotes:
notes.push(note);
fs.writeFileSync('notes-data.json',JSON.stringify(notes));
WecouldhavebrokenJSON.stringfy(notes)outintoits ownvariable
andreferencedthevariableintheabovestatement,butsincewe'll
onlybeusingitinoneplace,Ifindthisisthebettersolution.
At this point, when we add a new note, it will update the notes-data.JSON file,
whichwillbecreatedonthemachinesinceitdoesnotexist,andthenotewillsit
inside it. Now, it's important to note that currently every time you add a new
note,itwillwipeall existingonesbecausewenever loadin theexistingones,
butwecangetstartedtestingthatthisnoteworksasexpected.
I'll save the file, and over inside of Terminal, we can run this file using node
app.js.Sincewewanttoaddanote,wewillbeusingthataddcommandwhichwe
setup,thenwe'llspecifyourtitleandourbody.Thetitleflagcangetsetequal
tosecret,andforthebodyflag,I'llsetitequaltotheSomebodyherestring,asshown
here:
nodeapp.jsadd--title=secret--body="Somebodyhere"
Now,whenwerunthiscommandfromTerminal,we'llseewhatwe'dexpect:
Asshownintheprecedingscreenshot,weseeacoupleofthefilecommandswe
added: we see that the add command was executed, and we have our Yargs
arguments.Thetitleandbodyargumentsalsoshowup.InsideAtom,wealsosee
thatwehaveanewnotes-data.jsonfile,andinthefollowingscreenshot,wehave
ournote,withthesecrettitleandtheSomebodyherebody:
ThisisthefirststepinwiringupthataddNotefunction.Wehaveanexistingnotes
fileandwedowanttotakeadvantageofthesenotes.Ifnotesalreadyexist,we
don't want to simply wipe them every time someone adds a new note. This
meansthatinnotes.js,earlieratthebeginningoftheaddNotefunction,we'llfetch
thosenotes.
Fetchingnewnotes
I'lladdcodeforfetchingnewnoteswhereIdefinethenotesand notevariables.
As shown in the following code, we'll use fs.readFileSync, which we've already
explored.Thiswilltakethefilename,inour case,notes-data.JSON.Now,wewill
want to store the return value from readFileSync on a variable; I'll call that
variable,notesString:
varnotesString=fs.readFileSync('notes-data.json');
Since this is the string version, we haven't passed it through the JSON.parse
method. So, I can set notes (the variable we defined earlier in addNote function)
equaltothereturnvaluefromtheJSON.parsemethod.ThenJSON.parsewilltakethe
stringfromthefilewereadanditwillparseitintoanarray;wecouldpass in
notesStringjustlikethis:
notes=JSON.parse(notesString);
With this in place, adding a new note is no longer going to remove all of the
notesthatwerealreadythere.
OverinTerminal,I'llusetheuparrowkeytoloadinthelastcommand,andI'll
navigateovertothetitleflagandchangeittosecret2andrerunthecommand:
nodeapp.jsadd--title=secret2--body="Somebodyhere"
InAtom,thistimeyoucanseewenowhavetwonotesinsideofourfile:
Wehave an array with two objects; the first one has the title of secretandthe
secondonehasthetitleofsecret2,whichisbrilliant!
Tryingandcatchingcodeblock
Now,ifthenotes-data.jsonfiledoesnotexist,whichitwon'twhentheuserfirst
runs the command, the program will crash, as shown in the following code
output.Wecanprovethisbysimplyrerunningthelastcommandafterdeleting
thenote-data.JSONfile:
Righthere,youcanseewe'reactuallygettingaJavaScripterror,nosuchfileor
directory;it'stryingtoopenupthenotes-data.JSONfile,butwithoutmuchsuccess.
To fix this, we'll use a try-catch statement from JavaScript, which hopefully
you'veseeninthepast.Tobrushupthis,let'sgooveritreallyquick.
To create a try-catch statement, all you do is you type try, which is a reserved
keyword, and then you open and close a set of curly braces. Inside the curly
bracesisthecodethatwillrun.Thisisthecodethatmayormaynotthrow an
error. Next, you'll specify the catch block. Now, the catch block will take an
argument,anerrorargument,anditalsohasacodeblockthatruns:
try{
}catch(e){
}
Thiscodewillrunifandonlyifoneofyourerrorsintryactuallyoccurs.So,if
weloadthefileusingreadFileSyncandthefileexists,that'sfine,catchblockwill
never run. If it fails, catch block will run and we can do something to recover
fromthaterror.Withthisinplace,allwe'lldoismovethenoteStringvariableand
theJSON.parsestatementsintotry,asshownhere:
try{
varnotesString=fs.readFileSync('notes-data.json');
notes=JSON.parse(notesString);
}catch(e){
}
That'sit;nothingelseneedstohappen.Wedon'tneedtoputanycodeincatch,
although you do need to define the catch block. Now, let's take a look at what
happenswhenwerunthewholecode.
The first thing that happens is that we create our static variables—nothing
specialthere—thenwetrytoloadinthefile.IfthenotesStringfunctionfails,that
isfinebecausewealreadydefinednotestobeanemptyarray.Ifthefiledoesn't
exist and it fails, then we probably want an empty array for notes anyways,
becauseclearlytherearenonotes,andthere'snofile.
Nextup,we'llparsethatdataintonotes.Thereisachancethatthiswillfailif
there'sinvaliddatainthenotes-data.JSONfile,sothetwolinescanhaveproblems.
Byputtingthemintry-catch,we'rebasicallyguaranteeingthattheprogramisn't
goingtoworkunexpectedly,whetherthefiledoesordoesn'texist,butitcontains
corrupteddata.
With this in place, we can now save notes and rerun that previous command.
NotethatIdonothavethenotes-datafileinplace.WhenIrunthecommand,we
don'tseeanyerrors,everythingseemstorunasexpected:
WhenyounowvisitAtom,youcanseethatthenotes-datafiledoesindeedexist,
andthedatainsideitlooksgreat:
Thisisallweneedtodotofetchthenotes,updatethenoteswiththenewnote,
andfinallysavethenotestothescreen.
Now, there is still a slight problem with addNote. Currently, addNote allows for
duplicate titles; I could already have a note in the JSON file with the title of
secret.Icancomealongandtrytoaddanewnotewiththetitleofsecretandit
willnotthrowanerror.WhatI'dliketodoistomakethetitleunique,sothatif
there'salreadyanotewiththattitle,itwillthrowanerror,lettingyouknowthat
youneedtocreateanotewithadifferenttitle.
Makingthetitleunique
The first step to make the title unique will be to loop through all of the notes
afterwe load them in and check whether there are any duplicates. If there are
duplicates,we'llnotcallthefollowingtwolines:
notes.push(note);
fs.writeFileSync('notes-data.json',JSON.stringify(notes));
Iftherearenoduplicatesthenit'sfine,wewillcallbothofthelinesshowninthe
precedingcodeblock,updatingthenotes-datafile.
Now,we'llberefactoringthisfunctiondowntheline.Thingsaregettingalittle
wonky and a little out of control, but for the moment, we can add this
functionalityrightinto thefunction.Let'sgoaheadandmakeavariablecalled
duplicateNotes.
TheduplicateNotesvariablewilleventuallystoreanarraywithallofthenotesthat
alreadyexistinsidethenotesarraythathavethetitleofthenoteyou'retryingto
create.Now,thismeansthatiftheduplicateNotesarrayhasanyitems,that'sbad.
This means that the note already exists and we should not add the note. The
duplicateNotesvariablewill getset equalto acall tonotes, whichisour array of
notes.filter:
varduplicateNotes=notes.filter();
Thefiltermethodisanarraymethodthattakesacallback.We'lluse anarrow
function,andthatcallbackwillgetcalledwiththeargument.Inthiscase,itwill
be the singular version; if I have an array of notes, it will be called with an
individualnote:
varduplicateNotes=notes.filter((note)=>{
});
This function gets called once for every item in the array, and you have the
opportunitytoreturneithertrueorfalse.Ifyoureturntrue,itwillkeepthatitem
in the array, which will eventually get saved into duplicateNotes. If you return
false, the new array it generates will not have that item inside duplicateNotes
variable.Allwewanttodoistoreturntrueifthetitlesmatch,whichmeansthat
wecanreturnnote.title===title,asshownhere:
varduplicateNotes=notes.filter((note)=>{
returnnote.title===title;
});
Ifthetitlesareequal,thentheprecedingreturnstatementwillresultastrueand
theitemwillbekeptinthearray,whichmeansthatthereareduplicatenotes.If
thetitlesarenotequal,whichismostlikelythecase,thestatementwillresultas
false,whichmeansthattherearenoduplicatenotes.Now,wecansimplifythisa
littlemoreusingarrowfunctions.
Arrow functions actually allow you to remove the curly braces if
youonlyhaveonestatement.
I'llusethearrowfunction,asshownhere:
varduplicateNotes=notes.filter((note)=>note.title===title);
Here,Ihavedeletedeverythingexceptnote.title===titleandaddedthisinfront
ofthearrowfunctionsyntax.
ThisisperfectlyvalidusingES6arrowfunctions.Youhaveyourargumentson
theleft, the arrow, and on the right, you have one expression. The expression
doesn'ttake a semicolon and it's automatically returned as the function result.
Thismeansthat the codewehave here is identicaltothe code we had earlier,
onlyit'smuchsimpleranditonlytakesuponeline.
Now that we have this in place, we can go ahead and check the length of the
duplicateNotesvariable.IfthelengthofduplicateNotesisgreaterthan0,thismeans
thatwedon'twanttosavethenotebecauseanotealreadyexistswiththattitle.If
itis0,we'llsavethenote.
if(duplicateNotes.length===0){
}
Here,insidetheifcondition,we'recomparingthenoteslengthwiththenumber
zero.Iftheyareequal,thenwedowanttopushthenoteontothenotesarrayand
savethefile.I'llcutthefollowingtwolines:
notes.push(note);
fs.writeFileSync('notes-data.json',JSON.stringify(notes));
Let'spastethemrightinsideoftheifstatement,asshownhere:
if(duplicateNotes.length===0){
notes.push(note);
fs.writeFileSync('notes-data.json',JSON.stringify(notes));
}
Ifthey'renotequal,that'sokaytoo;inthatcasewe'lldonothing.
Withthisinplace,wecannowsaveourfileandtestthisfunctionalityout.We
haveournotes-data.jsonfile,andthisfilealreadyhasanotewithatitleofsecret2.
Let'srerunthepreviouscommandtotrytoaddanewnotewiththatsametitle:
nodeapp.jsadd--title=secret2--body="Somebodyhere"
You'reinTerminal,sowe'llheadbackintoourJSONfile.Youcanseerighthere
thatwestilljusthaveonenote:
Nowallthetitlesinsideofourapplicationwillbeunique,sowecanusethese
titlestofetchanddeletenotes.
Let'sgo aheadand testthatother notescan stillbe added.I'llchange thetitle
flagfromsecret2tosecret,andrunthatcommand:
nodeapp.jsadd--title=secret--body="Somebodyhere"
Insideournotes-datafile,youcanseebothnotesshowup:
AsImentionedearlier,nextwewillbedoingsomerefactoring,sincethecode
thatloadsthefile,andthecodethatsavesthefile,willbothbeusedinmostof
thefunctionswehave definedand/orwilldefine (thatis,thegetAll,getNoteand
removeNotefunctions).
Refactoring
In the previous section, you created the addNote function, which works well. It
starts by creating some static variables, then we fetch any existing notes, we
checkforduplicates,andiftherearenone,wepushitontothelist,andthenwe
savethedatabackintothefilesystem.
Theonlyproblemisthatwe'llbedoingalotofthesestepsoverandoveragain
foreverymethod.Forexample,withgetAll,theideaistofetchallofthenotes,
andsendthembacktoapp.jssoitcanprintthemtothescreenfortheuser.The
firstthingwe'lltodoinsideofthegetAllstatementishavethesamecode;we'll
haveourtry-catchblocktofetchtheexistingnotes.
Now, this is a problem because we'll be repeating code throughout the
application.Itwillbebesttobreakout thefetchingofnotes andthesavingof
notesintoseparatefunctionsthatwecancallinmultiplelocations.
Moving functionality into
individualfunctions
Toresolvetheproblem,I'dliketogetstartedbycreatingtwonewfunctions:
fetchNotes
saveNotes
Thefirstfunction,fetchNotes,willbeanarrowfunction,anditwillnottotakeany
argumentssinceitwillbefetchingnotesfromthefilesystem,asshownhere:
varfetchNotes=()=>{
};
Thesecondfunction,saveNotes,willneedtotakeanargument.Itwillneedtotake
thenotesarrayyouwanttosavetothefilesystem.We'llsetitequaltoanarrow
function,andthenwe'llprovideourargument,whichIwillnamenotes,asshown
here:
varsaveNotes=(notes)=>{
};
Nowthatwehavethesetwofunctions,wecangoaheadandstartmovingsome
ofthefunctionalityfromaddNoteupintotheindividualfunctions.
WorkingwithfetchNotes
Firstup,let'sdofetchNotes,whichwillneedthefollowingtry-catchblock.
I'llactuallycutitoutofaddNoteandpasteitinthefetchNotesfunction,asshown
here:
varfetchNotes=()=>{
try{
varnotesString=fs.readFileSync('notes-data.json');
notes=JSON.parse(notesString);
}catch(e){
}
};
This alone is not enough, because currently we don't return anything from the
function.Whatwewanttodoistoreturnthenotes.Thismeansthatinsteadof
saving the result from JSON.parse onto the notes variable, which we haven't
defined,we'llsimplyreturnittothecallingfunction,asshownhere:
varfetchNotes=()=>{
try{
varnotesString=fs.readFileSync('notes-data.json');
returnJSON.parse(notesString);
}catch(e){
}
};
So,ifIcallfetchNotesintheaddNotefunction,shownasfollows,Iwillgetthenotes
arraybecauseofthereturnstatementintheprecedingcode.
Now,iftherearenonotes,maybethere'snofileatall;orthereisafile,butthe
dataisn'tJSON,wecanreturnanemptyarray.We'lladdareturnstatementinside
ofcatch,asshowninthefollowingcodeblock,becauseremember,catchrunsif
anythinginsidetryfails:
varfetchNotes=()=>{
try{
varnotesString=fs.readFileSync('notes-data.json');
returnJSON.parse(notesString);
}catch(e){
return[];
}
};
Now,thisletsussimplifyaddNoteevenfurther.Wecanremovetheemptyspace
and we can take the array that we set on the notes variable and remove it and
insteadcallfetchNotes,asshownhere:
varaddNote=(title,body)=>{
varnotes=fetchNotes();
varnote={
title,
body
};
Withthisinplace,wenowhavetheexactsamefunctionalitywehadbefore,but
wehaveareusablefunction,fetchNotes,whichwecanuseintheaddNotefunction
tohandletheothercommandsthatourappwillsupport.
Instead of copying code and having it in multiple places in your file, we've
brokenitintooneplace.Ifweeverwanttochangehowthisfunctionalityworks,
whetherwewanttochangethefilenameorsomeofthelogicsuchasthetry-catch
block,wecanchangeitonceinsteadofhavingtochangeitineveryfunctionwe
have.
WorkingwithsaveNotes
Now, the same thing will go for saveNotes just as in the case of the fetchNotes
function. The saveNotes function will take the notes variable and it will say this
using fs.writeFileSync. I will cut out the line in addNote that does this (that is,
fs.writeFileSync('notes-data.json',JSON.stringfy(notes));)andpasteitinthesaveNotes
function,asshownhere:
varsaveNotes=(notes)=>{
fs.writeFileSync('notes-data.json',JSON.stringify(notes));
};
Now,saveNotesdoesn'tneedtoreturnanything.Inthiscase,we'llcopythelinein
saveNotes and then call saveNotes in the if statement of the addNote function, as
showninthefollowingcode:
if(duplicateNotes.length===0){
notes.push(note);
saveNotes();
}
This might seem like overkill, we've essentially taken one line and replaced it
withadifferentline,butitisagoodideatostartgettinginthehabitofcreating
reusablefunctions.
Now,callingsaveNoteswithnodataisnotgoingtowork,wewanttopassinthe
notesvariable,whichisournotesarraydefinedearlierinthesaveNotesfunction:
if(duplicateNotes.length===0){
notes.push(note);
saveNotes(notes);
}
Withthisinplace,theaddNotefunctionshouldnowworkasitdidbeforewedid
anyofourrefactoring.
Testingthefunctionality
Thenextstepintheprocesswillbetotestthisoutbycreatinganewnote.We
alreadyhavetwonotes,withatitleofsecretandatitleofsecret2innotes-data.json,
let'smakeathirdoneusingthenodeapp.jscommandinTerminal.We'llusethe
addcommandandpassinatitleoftobuyandabodyoffood,asshownhere:
nodeapp.jsadd--title="tobuy"--body="food"
Thisshouldcreateanewnote,andifIrunthecommand,youcanseewedon't
haveanyobviouserrors:
Insideofournotes-data.jsonfile,ifIscrolltotheright,wehave ourbrandnew
noteasatitleoftobuyandabodyoffood:
So, everything is working as expected even though we've refactored the code.
Now,thenextthingIwanttodoinsideaddNoteistakeamomenttoreturnthenote
that'sbeingadded,andthatwillhappenrightaftersaveNotescomesback.Sowe'll
returnnote:
if(duplicateNotes.length===0){
notes.push(note);
saveNotes(notes);
returnnote;
}
Thisnoteobjectwillgetreturnedtowhoevercalledthefunction,andinthiscase,
it will get returnedtoapp.js, where we called it in the if else block of the add
commandintheapp.js file.Wecanmakea variableto storethis resultandwe
cancallitnote:
if(command==='add')
varnote=notes.addNote(argv.title,argv.body);
Ifnoteexists,thenweknowthatthenotewascreated.Thismeansthatwecango
aheadandprintamessage,likeNotecreated,andwecanprintthenotetitleandthe
notebody.Now,ifnotedoesnotexist,ifit'sundefined,thismeansthattherewas
aduplicateandthattitlealreadyexists.Ifthat'sthecase,Iwantyoutoprintan
errormessagesuchasNotetitlealreadyinuse.
There'satonofdifferentwaysyoucoulddothis.Thegoal,though,
is to print two different messages depending on whether or not a
notewasreturned.
Now,insideaddNote,iftheduplicateNotesifstatementneverruns,wedon'thavean
explicit call to return. But as you know, in JavaScript, if you don't call return,
thenundefinedautomaticallyisreturned.ThismeansthatifduplicateNotes.lengthis
notequaltozero,undefinedwillbereturnedandwecanusethatasthecondition
forourstatement.
The first thing I'll do here is to create an if statement, right next to the note
variablewedefinedinapp.js:
if(command==='add'){
varnote=notes.addNote(argv.title,argv.body);
if(note){
}
Thiswillbeanobjectifthingswentwell,anditwillbeundefinedifthingswent
poorly.Thiscodeinhereisonlyevergoingtorunifit'sanobject.TheUndefined
resultwillfailtheconditioninsideofJavaScript.
Now,ifthenotewascreatedsuccessfully,whatwe'lldoistoprintalittlemessage
tothescreen,usingthefollowingconsole.logstatement:
if(note){
console.log('Notecreated');
}
Ifthingswentpoorly,insidetheelseclause,wecancallconsole.log,andwe can
printsomethinglikeNotetitletaken,asshownhere:
if(note){
console.log('Notecreated');
}else{
console.log('Notetitletaken');
}
Now, the other thing that we want to do if things went well is print the notes
content.I'lldo this by first using console.log to print acoupleofhyphens.This
willcreatealittlespaceabovemynote.ThenIcanuseconsole.logtwice:thefirst
timewe'llprintthetitle,I'lladdTitle:asastringtoshowyouwhatexactlyyou're
seeing,thenIcanconcatenatethetitle,whichwehaveaccesstoinnote.title,as
showninthiscode:
if(note){
console.log('Notecreated');
console.log('--');
console.log('Title:'+note.title);
Now, the preceding syntax uses an ES5 syntax; we can swap this out with an
ES6syntaxusing what we've already talked about: template strings. We'll add
Title,acolon,andthenwecanuseourdollarsignwithourcurlybracestoinject
thenote.titlevariable,asshownhere:
console.log(`Title:${note.title}`);
Similarly,I'lladdnote.bodyafterthistoprintoutthebodyofthenote.Withthisin
place,thecodeshouldlooklike:
if(command==='add'){
varnote=note.addNote(argv.title,argv.body);
if(note){
console.log('Notecreated');
console.log('--');
console.log(`Title:${note.title}`);
console.log(`Body:${note.body}`);
}else{
console.log('Notetitletaken');
}
Now,weshouldbeabletorunourappandseebothofthetitleandbodynotes
printed.InTerminal,I'll rerun theprevious command.Thiswill trytocreate a
notewithtobuy,whichalreadyexists,soweshouldgetanerrormessage,and
righthereyoucanseeNotetitletaken:
Now,wecanrerunthecommand,changingthetitletosomethingelse,suchasto
buyfromstore.Thisisauniquenotetitlesothenoteshouldgetcreatedwithoutany
problems:
nodeapp.jsadd--title="tobuyfromstore"--body="food"
Asshownintheprecedingoutput,youcanseethatwegetjustthat:wehaveour
Notecreatedmessage,ourlittlespacer,andourtitlealongwiththebody.
TheaddNotecommandis nowcomplete.Wehave anoutputwhenthe command
actuallyfinishes,andwehaveallthecodethatrunsbehindthescenestoaddthe
notetothedatathatgetsstoredinourfile.
Summary
In this chapter, you learned that parsing in process.argv can be a real pain. We
wouldhavetowritealotofmanualcodetoparseoutthosehyphens,theequal
signs, and the optional quotes. However, yargscan do all of that for us and it
putsit onareally simpleobject wecan access.Youalso learnedhow towork
withJSONinsideNode.js.
Next, we filled out the addNote function. We're able to add notes using the
commandline,andwe'reabletosavethosenotesintoaJSONfile.Finally,we
pulled out a lot of the code from addNote into separate functions, fetchNotesand
saveNotes,which are now separate, andthey'reable to be reused throughout the
code.Whenwestartfillingouttheothermethods,wecansimplycallfetchNotes
andsaveNotesinsteadofhavingtocopythecontentsoverandoveragaintoevery
newmethod.
In the next chapter, we'll continue our journey on node fundamentals. We'll
exploresomemoreconceptsrelatedtonode,suchasdebugging;we'llworkon
thereadandremove notes commands.Apart from this,we'llalso learn aboutthe
advancedfeaturesofyargsandthearrowfunction.
NodeFundamentals–Part3
We start adding support for all the other commands inside of the notes
application.We'lltakealookathowwecancreateourreadcommand.Theread
commandwillberesponsibleforfetchingthebodyofanindividualnote.Itwill
fetchallthenotesandprintthemtothescreen.Now,asidefromallofthat,we'll
belookingatdebuggingbrokenapps,andwe'lllookatsomenewES6features.
You'lllearnhowtousethebuilt-inNodedebugger.
Then,youwilllearnalittlebitmoreabouthowwecanconfigureyargsforthe
command-lineinterface applications. We'll learn how to set up the commands,
theirdescriptions,andthearguments.We'llbeabletosetvariouspropertieson
thearguments,forexample,whetherornotthey'rerequired,andothers.
Removinganote
Inthissection,youwillwritethecodeforremovinganotewhensomeoneuses
thatremovecommand,andtheypassinthetitleofthenotetheywanttoremove.
In the previous chapter, we already created some utility functions that help us
withfetchingandsavingnotes,sothecodeshouldactuallybeprettysimple.
UsingtheremoveNotefunction
ThefirststepintheprocessistofillouttheremoveNotefunction,whichwedefined
inthepreviouschapters,andthiswillbeyourchallenge.Let'sremoveconsole.log
fromtheremoveNotefunctioninthenotes.jsfile.Youonlyneedtowritethreelines
ofcodetogetthisdone.
Now,thefirstlinewillfetchthenotes,thenthejobwillbetofilteroutthenotes,
removingtheonewithtitleofargument.Thatmeanswewanttogothroughall
ofthenotesinthenotesarray,andifanyofthemhaveatitlethatmatchesthe
titlewewanttoremove,wewanttogetridofthem.Andthiscanbedoneusing
thenotes.filterfunctionweusedearlier.Allwehavetodoisswitchtheequality
statementintheduplicateNotesfunctionfromequalsto notequals,andthiscode
willdojustthat.
Itwillgothroughthenotesarray.Everytimeitfindsanotethatdoesn'tmatch
thetitleitwillkeepit,whichiswhatwewant,andifitdoesfindthetitleitwill
returnfalse and removeitfromthe array. And then we will add the thirdline,
whichistosavethenewnotesarray:
varremoveNote=(title)=>{
//fetchnotes
//filternotes,removingtheonewithtitleofargument
//savenewnotesarray
};
The preceding code lines are the only three lines you need to fill out. Don't
worryaboutreturninganything fromremoveNoteor fillingoutanything insideof
app.js.
ThefirstthingwewilldoforthefetchNoteslineistocreateavariablecallednotes,
just like we did in addNote in the previous chapter, and we'll set it equal to the
returnresultfromfetchNotes:
varremoveNote=(title)=>{
varnotes=fetchNotes();
//filternotes,removingtheonewithtitleofargument
//savenewnotesarray
};
Atthispointournotesvariablestoresanarrayofallofthenotes.Thenextthing
weneedtodoisfilterournotes.
Ifthereisanotethathasthistitle,wewanttoremoveit.Thiswillbedoneby
creating a new variable, and I'll call this one filteredNotes. Here we'll set
filteredNotesequal totheresultthatwillcomebackfrom notes.filter,whichwe
alreadyuseduppreviously:
varremoveNote=(title)=>{
varnotes=fetchNotes();
//filternotes,removingtheonewithtitleofargument
varfilteredNotes=notes.filter();
//savenewnotesarray
};
Weknowthatnotes.filtertakesafunctionasitsoneandonlyargument,andthat
functiongetscalledwiththeindividualiteminthearray.Inthiscaseitwouldbe
anote.AndwecandothisallononelineusingtheES6arrowsyntax.
If we have only one statement, we don't need to open and close
curlybraces.
Thatmeansrightherewecanreturntrueifnote.titledoesnotequalthetitlethat's
passedintothefunction:
varremoveNote=(title)=>{
varnotes=fetchNotes();
varfilteredNotes=notes.filter((note)=>note.title!==title);
//savenewnotesarray
};
ThiswillpopulatefilteredNoteswithallofthenoteswhosetitlesdonotmatchthe
onepassedin.Ifthetitledoesmatchthetitlepassedin,itwillnotbeaddedto
filteredNotesbecauseofourfilterfunction.
ThelastthingtodoistocallsaveNotes.Righthere,we'llcallsaveNotespassingin
thenewnotesarraywhichwehaveunderthefilteredNotesvariable:
varremoveNote=(title)=>{
varnotes=fetchNotes();
varfilteredNotes=notes.filter((note)=>note.title!==title);
saveNotes(filteredNotes);
//savenewnotesarray
};
If we were to pass in notes, it wouldn't work as expected; we're filtering the
notesout but we're not actually saving those notes, so it will not get removed
fromtheJSON. We need to pass filteredNotes as shown in the preceding code.
Andwecantestthesebysavingthefileandtryingtoremoveoneofournotes.
I'lltrytoremovesecret2fromthenotes-data.jsonfile.Thatmeansallweneedto
doisrunthecommand,whichwecalledremove,that isspecifiedover inapp.js,
(refertothefollowingcodeimage,andthenitwillcallourfunction).
I'll run Node with app.js, and we'll pass in the remove command. The only
argumentweneedtoprovideforremoveisthetitle;there'snoneedtoprovide
thebody.I'llsetthisequaltosecret2:
nodeapp.jsremove--title=secret2
Asshowninthescreenshot,ifIhitenteryoucanseewedon'tgetanyoutput.
Althoughwedohavethecommandremoveprinting,thereisnomessagesaying
whetherornotanotewasremoved,butwe'lladdthatlaterinthesection.
Fornow,wecancheckthedata.Andrighthereyoucanseesecret2isnowherein
sight:
Thismeansourremove methodisindeedworking asexpected.Itremovedthe
notewhosetitle matchedand itkeptall thenotes whose titlewas notequalto
secret2,exactlywhatwewanted.
Printing a message of removing
notes
Now,thenextthingwe'lldoisprintamessagedependingonwhetherornota
notewasactuallyremoved.Thatmeansapp.js,whichcallstheremoveNotefunction,
willneedtoknow whetherornotanotewasremoved. Andhowdowefigure
that out? How can we possibly return that given the information we have in
notes.jsremoveNotesfunction?
Well,wecan,becausewehavetworeallyimportantpiecesofinformation.We
havethe length of the original notes array and we have the length of the new
notesarray. If they're equal then we can assume that no note was removed. If
they are not equal, we'll assume that a note was removed. And that is exactly
whatwe'lldo.
IftheremoveNotefunctionreturnstrue,thatmeansanotewasremoved;ifitreturns
false,thatmeansanotewasnotremoved.IntheremoveNotesfunctionwecanadd
return,asshowninthefollowingcode.We'llcheckifnotes.lengthdoesnotequal
filteredNotes.length:
varremoveNote=(title)=>{
varnotes=fetchNotes();
varfilteredNotes=notes.filter((note)=>note.title!==title);
saveNotes(filteredNotes);

returnnotes.length!==filteredNotes.length;
};
Ifthey'renotequalitwillreturntrue,whichiswhatwewantbecauseanotewas
removed.Ifthey'reequalitwillreturnfalse,whichisgreat.
Now, inside of app.js we can add a few lines in the removeNote,else if block to
maketheoutputforthiscommandalittlenicer.Thefirstthingtodoistostore
thatBoolean.I'llmakeavariablecallednoteRemovedandwe'llsetthatequaltothe
return,resultasshowninthefollowingcode,whichwilleitherbetrueorfalse:
}elseif(command=='remove'){
varnoteRemoved=notes.removeNote(argv.title);
}
Onthenextline,wecancreateourmessage,andI'lldothisallononelineusing
theternary operator. Now, theternaryoperator letsyou specify acondition. In
our case, we'll use a var message and it will be set equal to the condition
noteRemoved,whichwillbetrueifanotewasremovedandfalseifitwasn't.
Now,theternaryoperatorcanbea littleconfusing,butit'sreally
useful inside JavaScript and Node.js. The format for the ternary
operator is first we add the condition, question mark, the truthy
expressiontorun,colon,andthenthefalsyexpressiontorun.
Afterthecondition,we'llputaspacewithaquestionmarkandaspace;thisis
thestatementthatwillrunifit'strue.IfthenoteRemovedconditionpasses,whatwe
wanttodoissetmessageequaltoNotewasremoved:
varmessage=noteRemoved?'Notewasremoved':
Now,ifnoteRemovedisfalse,wecanspecifythatconditionrightafterthecolonin
thepreviousstatement.Here,ifthereisnonoteremovedwe'llusethetextNote
notfound:
varmessage=noteRemoved?'Notewasremoved':'Notenotfound';
Nowwiththisinplace,wecantestoutourmessage.Thelastthingtodoisprint
themessagetothescreenusingconsole.logpassinginmessage:
varnoteRemoved=notes.removeNote(argv.title);
varmessage=noteRemoved?'Notewasremoved':'Notenotfound';
console.log(message);
This lets us avoid if statements that make our else-if clause to remove
unnecessarilycomplex.
Backinside ofAtom wecanrerun thelast command,and inthiscase nonote
willgetremovedbecausewealreadydeletedit.AndwhenIrunit,youcansee
thatNotenotfoundprintstothescreen:
NowI'llremoveanotethatdoesexist;innotes-data.jsonIhaveanotewithatitle
ofsecretasshownhere:
Let'srerunthecommandremovingthe2fromthetitleinTerminal.WhenIrun
thiscommand,youcanseeNotewasremovedprintstothescreen:
Thatisitforthissection;wenowhaveourremovecommandinplace.
Readingnote
In this section, you will be responsible for filling out the rest of the read
command. Now, the read command does have an else-if block to find in app.js
wherewecallgetNote:
}elseif(command==='read'){
notes.getNote(argv.title);
getNote is defined over inside notes.js, even though currently it just prints out
somedummytext:
vargetNote=(title)=>{
console.log('Gettingnote',title);
};
Whatyou'llneedtodointhissectioniswireupbothofthesefunctions.
Firstup,youwillneedtodosomethingwiththereturnvaluefromgetNote.Our
getNotefunctionwillreturnthenoteobjectifitfindsit.Ifitdoesn't,itwillreturn
undefinedjustlikewedoforaddNotediscussedinthesectionAddingandsaving
note,inthepreviouschapter.
After you store that value, you'll do some printing using console.log, similar to
whatwehavehere:
if(command==='add'){
varnote=notes.addNote(argv.title,argv.body);
if(note){
console.log('Notecreated');
console.log('--');
console.log(`Title:${note.title}`);
console.log(`Body:${note.body}`);
}else{
console.log('Notetitletaken');
}
Obviously,NotecreatedwillbesomethinglikeNotereadandNotetitletakenwillbe
something like Note not found, but the general flow is going to be exactly the
same.Now, once you have that wired up inside of app.js, you can move on to
notes.js,fillingoutthefunction.
Now,thefunctioninsideofnotes.jsisn'tgoingtobethatcomplex.Allyouneed
todoisfetchthenotes,likewe'vedoneinpreviousmethods,thenyou'regoing
tousenotes.filter,which weexplored toonly returnnotes whosetitle matches
thetitle passed inasthe argument. Now, in our case this is either going to be
zeronotes,whichmeansthenoteisnotfound,orit'sgoingtobeonenote,which
meanswe'vefoundthenotethatthepersonwantstoreturn.
Next,wedoneedtoreturnthatnote.It'simportanttorememberthereturnvalue
fromnotes.filterisalwaysgoingtobeanarray,evenifthatarrayonlyhasone
item.Whatyou'regoingtoneedtodoisreturnthefirstiteminthearray.Ifthat
itemdoesn'texistthat'sfine,it'llreturn undefined,aswewant.Ifitdoesexist,
great, that means we found the note. This method only requires three lines of
code,oneforfetching,oneforfiltering,andthereturnstatement.Now,onceyou
haveallthatdonewe'lltestitout.
UsingthegetNotefunction
Let'sworkonthismethod.Now,thefirstthingI'lldoisfillout,insideofapp.js,a
variablecallednotewhichisgoingtostorethereturnvaluefromgetNote:
}elseif(command==='read'){
varnote=notes.getNote(argv.title);
Now,thiscouldbeanindividualnoteobjectoritcouldbeundefined.Inthenext
line,Icanuseanifstatementtoprintthemessageifitexists,orifitdoesnot
exist.I'lluseifnote,andIamgoingtoattachanelseclause:
}elseif(command==='read'){
varnote=notes.getNote(argv.title);
if(note){

}else{

}
Thiselseclausewillberesponsibleforprintinganerrorifthenoteisnotfound.
Let'sgetstartedwiththatfirstsinceit'sprettysimple,console.log,Notenotfound,as
shownhere:
if(note){

}else{
console.log('Notenotfound');
}
Nowthatwehaveourelseclausefilledoutwecanfillouttheifstatement.For
this,I'llprintalittlemessage,console.log('Notefound')willgetthejobdone.Then
wecanmoveontoprintingtheactualnotedetails,andwealreadyhavethatcode
inplace.Wearegoingtoaddthehyphenatedspacer,thenwehaveournotetitle
andournotebodyasshownhere:
if(note){
console.log('Notefound');
console.log('--');
console.log(`Title:${note.title}`);
console.log(`Body:${note.body}`);
}else{
console.log('Notenotfound');
}
Nowthatwe'redonewiththeinsideofapp.js,wecanmoveintothenotes.jsfile
andfilloutthegetNotemethodbecausecurrentlyitdoesn'tdoanythingwiththe
titlethatgetspassedin.
Insidenotes,whatyouneededtodowasfilloutthosethreelines.Thefirstoneis
goingtoberesponsibleforfetchingthenotes.Wealreadyhavedidthatbefore
withthefetchNotesfunctionintheprevioussection:
vargetNote=(title)=>{
varnotes=fetchNotes();
};
Nowthatwehaveournotesinplacewe,cancallnotes.filter,returningallofthe
notes.I'llmakeavariablecalledfilteredNotes,settingitequaltonotes.filter.Now,
weknowthatthefiltermethodtakesafunction,I'lldefineanarrowfunction(=>)
justlikethis:
varfilteredNotes=notes.filter(()=>{
});
Insidethearrowfunction(=>),we'llgettheindividualnotepassedin,andwe'll
returntruewhenthe notetitle,thetitleofthenotewe foundinourJSONfile,
equals,usingtripleequals,title:
varfilteredNotes=notes.filter(()=>{
returnnote.title===title;
});
};
This will return true when the note title matches and false if it doesn't.
Alternatively,wecanusearrowfunctions,andweonlyhaveoneline,asshown
following,wherewereturnsomething;wecancutoutourcondition,removethe
curlybraces,andsimplypastethatconditionrighthere:
varfilteredNotes=notes.filter((note)=>note.title===title);
Thishastheexactsamefunctionality,onlyit'salotshorterandeasiertolookat.
Nowthatwehaveallofthedata,allweneedtodoisreturnsomething,andwe'll
return the first item in the filteredNotes array. Next, we'll grab the first item,
which is the index of zero, and then we just need to return it using the return
keyword:
vargetNote=(title)=>{
varnotes=fetchNotes();
varfilteredNotes=notes.filter((note)=>note.title===title);
returnfilteredNotes[0];
};
Now, there is a chance that filteredNotes,the first item, doesn't exist,andthat's
fine, it's going to return undefined, in which case our else clause will run,
printingNote not found.Ifthereisanote,great, that'sthenotewe wanttoprint,
andoverinapp.jswedojustthat.
RunningthegetNotefunction
Now that we have this in place we can test out this brand new functionality
inside of Terminal by running our app using node app.js. I'll use the read
command,andI'llpassinatitleequaltosomestringthatIknowdoesnotexist
insideofatitleinthenotes-data.jsonfile:
nodeapp.jsread--title="somethinghere"
WhenIrunthecommand,wegetNotenotfound,asshownhere,andthisisexactly
whatwewant:
Now,ifIdotrytofetchanotewherethetitledoesexist,Iwouldexpectthatnote
tocomeback.
InthedatafileIhaveanotewithatitleoftobuy;let'strytofetchthatone.I'll
use the up arrow key to populate the previous command and replace the title
withtospace,buy,andhitenter:
Asshowninthepreviouscode,youcanseeNotefoundprintstothescreen,which
isfantastic.FollowingNotefoundwehaveourspacersandfollowingthatwehave
thetitle,whichistobuy,andthebody,whichisfood,exactlyasitappearsinside
ofthedatafile.Withthisinplace,wearedonewiththereadcommand.
TheDRYprinciple
Now, there is one more thingIwant to tackle before we wrap up thissection.
Insideapp.js we nowhavethesamecode in two places. We have the space or
titlebodyintheaddcommandaswellasinthereadcommand:
if(command==='add'){
varnote=notes.addNote(argv.title,argv.body);
if(note){
console.log('Notecreated');
console.log('--');
console.log(`Title:${note.title}`);
console.log(`Body:${note.body}`);
}else{
console.log('Notetitletaken');
}
}elseif(command==='list'){
notes.getAll();
}elseif(command==='read'){
varnote=notes.getNote(argv.title);
if(note){
console.log('Notefound');
console.log('--');
console.log(`Title:${note.title}`);
console.log(`Body:${note.body}`);
}else{
console.log('Notenotfound');
}
Whenyoufindyourselfcopyingandpastingcode,it'sprobablybesttobreakthat
out into a function that both locations call. This is the DRY principle, which
standsforDon'tRepeatYourself.
UsingthelogNotefunction
Inourcase,wearerepeatingourselves.Itwouldbebesttobreakthisoutintoa
functionthatwecancallfrombothplaces.Inordertodothis,allwe'regoingto
doismakeafunctioninnotes.jscalledlogNote.
Now,innotes.js,downfollowingtheremoveNotefunction,wecanmakethatbrand
newfunctionavariable calledlogNote.Thisisgoingtobea functionthattakes
oneargument.Thisargument willbethe noteobject because wewant to print
boththetitleandthebody.Asshownhere,we'llexpectthenotetogetpassedin:
varlogNote=(note)=>{
};
Now,fillingoutthelogNotefunctionisgoingtobereallysimple,especiallywhen
you're solving a DRY issue, because you can simply take the code that's
repeated,cutitout,andpasteitrightinsidethelogNotefunction.Inthiscasethe
variablenameslineupalready,sothereisnoneedtochangeanything:
varlogNote=(note)=>{
console.log('--');
console.log(`Title:${note.title}`);
console.log(`Body:${note.body}`);
};
Now that we have the logNote function in place, we can change things over in
app.js.Inapp.js, where we have removed the console.log statements we can call
notes.logNote,passinginthenoteobjectjustlikethis:
elseif(command==='read'){
varnote=notes.getNote(argv.title);
if(note){
console.log('Notefound');
notes.logNote(note);
}else{
console.log('Notenotfound');
}
Andwecandothesamethingincaseoftheaddcommandifblock.Icanremove
thesethreeconsole.logstatementsandcallnotes.logNote,passinginnote:
if(command==='add'){
varnote=notes.addNote(argv.title,argv.body);
if(note){
console.log('Notecreated');
notes.logNote(note);
}else{
console.log('Notetitletaken');
}
And now that we have this in place, we can rerun our program and hopefully
whatweseeistheexactsamefunctionality.
ThelastthingtodobeforewereruntheprogramisexportthelogNotefunctionin
exportsmoduleinnotes.jsfile.LogNoteisgoingtogetexportedandwe'reusingthe
ES6syntaxtodothat:
module.exports={
addNote,
getAll,
getNote,
removeNote,
logNote
};
Withthisinplace,IcannowrerunthepreviouscommandfromTerminalusing
upandhitenter:
nodeapp.jsread--title="tobuy"
Asshown,wegetNotefoundprintingtothescreen,withthetitleandthebodyjust
likewehadbefore.I'malsogoingtotestouttheaddcommandtomakesurethat
one'sworking,nodeapp.jsadd;wewilluseatitleofthingstodoandabodyofgo
topostoffice:
nodeapp.jsadd--title="thingstodo"--body="gotopostoffice"
Now,whenIhitenter,wewouldexpectthesamelogtoprintasitdidbeforefor
theaddcommand,andthat'sexactlywhatweget:
Notecreatedprints,wegetourspacer,andthenwegetourtitleandourbody.
Inthenextsection,we'regoingtocoveroneofthemostimportanttopicsinthe
book;whichisdebugging.Knowinghowtoproperlydebugprogramsisgoingto
saveyouliterallyhundredsofhoursoveryourNode.jscareer.Debuggingcanbe
reallypainfulifyoudon'thavetherighttools,butonceyouknowhowit'sdone,
itreallyisn'tthatbadanditcansaveyouatonoftime.
Debugging
In this section, we're going to use the built-in debugger, which can look a little
complexbecauseit'sruninsideofthecommandline.Thatmeansthatyouhave
tousethecommand-lineinterface,whichisnotalwaysthemostpleasantthing
tolookat.Inthenextsection,though,wearegoingtobeinstallingathird-party
tool that uses Chrome DevTools in order to debug your Node app. That one
looksgreatbecausetheChromeDevToolsarefantastic.
Executing a program in debug
mode
Before going ahead, we will learn that we do need to create a place to play
aroundwithdebuggingandthat'sgoingtohappeninaplaygroundfile,sincethe
code we're going to write is not going to be important to the notes app itself.
InsidethenotesappI'llmakeanewfilecalleddebugging.js:
Indebugging.jswe'regoingtostartoffwithabasicexample.We'regoingtomake
anobjectcalledperson,andonthatobjectforthemoment,we'regoingtosetone
propertyname.Setitequaltoyourname,I'llsetmineequaltothestringAndrewas
shown:
varperson={
name:'Andrew'
};
Nextupwe'regoingtosetanotherproperty,butinthenextline,person.age.I'llset
mineequaltomyage,25:
varperson={
name:'Andrew'
};
person.age=25;
Then we're going to add another statement that changes the name, person.name
equalssomethinglikeMike:
varperson={
name:'Andrew'
};
person.age=25;
person.name='Mike';
Finally,we'regoingtoconsole.logthepersonobject,thecodeisgoingtolooklike
this:
varperson={
name:'Andrew'
};
person.age=25;
person.name='Mike';
console.log(person);
Now,weactuallyalreadyhaveaformofdebugginginthisexample,wehavea
console.logstatement.
Asyou'regoingthroughtheNodeapplicationdevelopmentprocess,youmayor
may not have used console.log to debug your app. Maybe something's not
workingasexpectedand youwanttofigureoutexactlywhatthatvariablehas
stored inside of it. For example, if you have a function that solves a math
problem, maybe at one part in the function the equation is wrong and you're
gettingadifferentresult.
Usingconsole.logcanbeaprettygreatwaytodothat,butit'ssuperlimited.We
canviewthatby runningit fromTerminal,I'll runthe followingcommand for
this:
nodeplayground/debugging.js
WhenIrunthefile,Idogetmyobjectprintedouttothescreen,whichisgreat,
but,asyouknow,ifyouwanttodebugsomethingbesidesthepersonobjectyou
havetoaddanotherconsole.logstatementinordertodothat.
Imagineyouhavesomethinglikeourapp.jsfile,youwanttoseewhatcommand
equals,thenyouwanttoseewhatargvequals,itcouldtakealotoftimetoadd
andremovethoseconsole.logstatements.Thereisabetterwaytodebug.Thisis
usingtheNodedebugger.Now,beforewemakeanychangestotheproject,we'll
takealookathowthedebuggerworksinsideofTerminal,andasIwarnedyouin
thebeginningofthesection,thebuilt-inNodedebugger,whileitiseffective,isa
littleuglyandhardtouse.
Fornow,though,wearegoingtoruntheappmuchthesameway,onlythistime
we'regoingtotypenodeinspect.Nodedebugisgoingtorunourappcompletely
differentlyfromtheregularNodecommand.We'rerunningthesamefileinthe
playgroundfolder,it'scalleddebugging.js:
nodeinspectplayground/debugging.js
Whenyouhitenter,youshouldseesomethinglikethis:
Intheoutput,wecanignorethefirsttwolines.Thisessentiallymeansthatthe
debugger was set up correctly and it's able to listen to the app running in the
background.
Next,wehaveourveryfirstlinebreakinplaygrounddebuggingonlineone,and
rightfollowingtoityoucanseelineonewithalittlecaret(>)nexttoit.When
you first run your app in debug mode, it pauses before it executes the first
statement.Whenwe'repausedonalinelikelineone,thatmeansthelinehasnot
executed,soatthispointintimewedon'tevenhavethepersonvariableinplace.
Now,asyoucanseeintheprecedingcode,wehaven'treturnedtothecommand
line,Nodeisstillwaitingforinput,andthereareafewdifferentcommandswe
canrun.Forexample,wecanrunn,whichisshortfornext.Youcantypen,hit
enter,andthismovesontothenextstatement.
The next statement we have, the statement on line one, was executed, so the
person variable does exist. Then I can use n again to go to the next statement
wherewedeclaretheperson.nameproperty,updatingitfromAndrewtoMike:
Notice,atthispoint,agedoesexistbecausethatlinehasalreadybeenexecuted.
Now,thencommandgoesstatementbystatementthroughyourentireprogram.
Ifyourealizethatyoudon'twanttodothatthroughthewholeprogram,which
couldtakealotoftime,youcanusec.Theccommandisshort forContinue,
andthatcontinuestotheveryendoftheprogram.Inthefollowingcode,youcan
seeourconsole.logstatementrunsthenameMikeandtheage25:
Thisisthat'saquickexampleofhowtousethedebugkeyword.
Now, we actually didn't do any debugging, we just ran through the program
sinceitisalittleforeignintermsofwritingthesecommands,suchasnextand
continue,Idecidedtodoadryrunoncewithnodebugging.Youcanusecontrol
+CtoquitthedebuggerandgetreturnedbacktoTerminal.
I'llusecleartoclearalltheoutput.Nowthatwehaveabasicideaabouthowwe
canexecutetheprogramindebugmode,let'stakealookathowwecanactually
dosomedebugging.
Workingwithdebugging
I'llreruntheprogram usingtheuparrowkeytwicetoreturntotheNode debug
command.Then,I'llruntheprogram,andI'llhitnexttwice,nandn:
Atthispointintime,weareonlineseven,thatiswherethelinebreakcurrently
is.Fromhere we can do some debuggingusinga command called repl,which
standsforRead Evaluate Print Loop.The repl command, in our case, brings
youtoanentirelyseparateareaofthedebugger.Whenyouhitityou'reessentially
inaNodeconsole:
You can run any Node commands, for example, I can use console.log to print
somethingliketest,andtestprintsuprightthere.
Icanmakeavariableathatisequalto1plus3,thenIcanreferenceaandIcan
seeit'sequalto4asshown:
Moreimportantly,wehaveaccesstothecurrentprogramasitsits,meaningasit
wasbeforelinesevenwasexecuted.Wecanusethistoprintoutperson,andas
showninthefollowingcode,youcanseetheperson'snameisAndrewbecauseline
sevenhasn'texecutedandtheageis25,exactlyasitappearsintheprogram:
This is where debugging gets really useful. Being able to look at the program
pausedatacertainpointintimeisgoingtomakeitreallyeasytospoterrors.I
coulddo anything I want, I could print out the person name property, and that
printsAndrewtothescreen,asshownhere:
Now, once again, we still have this problem. I have to hit next through the
program.Whenyouhaveareallylongprogram,therecouldliterallybehundreds
orthousandsofstatementsthatneedtorunbeforeyougettothepointyoucare
about.Obviouslythatisnotideal,sowe'regoingtolookatabetterway.
Let'squitreplusingcontrol+C;nowwe'rebackatthedebugger.
Fromherewearegoingtomakeaquickchangetoourapplicationindebugging.js.
Let'ssay wewantto pauseline seven betweenthe personageproperty update
andthepersonnamepropertyupdate.Inordertopause,whatwe'regoingtodo
isrunthestatementdebugger:
varperson={
name:'Andrew'
};
person.age=25;
debugger;
person.name='Mike';
console.log(person);
When you have a debugger statement exactly like previous, it tells the Node
debuggertostophere,whichmeansinsteadof usingn (next) togo statement by
statement,youcanusec(continue),whichisgoingto continueuntileitherthe
programexitsoritseesoneofthedebuggerkeywords.
Now, over in Terminal, we're going to rerun the program exactly like we did
before. This time around, instead of hitting n twice, we're going to use c to
continue:
Now,whenwefirstusedc,it wenttotheend oftheprogram,printingoutour
object.Thistimearoundit'sgoingtocontinueuntilitfindsthatdebuggerkeyword.
Now,wecanuserepl,accessanythingwelike,forexample,person.age,shownin
thiscode:
Once we're done debugging, we can quit and continue through the program.
Again,wecanusecontrol+Ctoquitreplandthedebugger.
All real debugging pretty much happens with the debugger keyword. You put it
wherever you want on your program, you run the program in debug mode,
eventually it gets to the debugger keyword and you do something. For example
youexploresomevariablevalues,yourunsomefunctions,oryouplayaround
withacodetofindtheerror.Noonereallyusesntoprintthroughtheprogram,
findingthelinethatcausestheproblem.Thattakeswaytoomuchtimeandit's
justnotrealistic.
Using debugger inside the notes
application
Nowthatyouknowalittlebitaboutthedebugger,Iwantyoutouseitinsideour
notesapplication.Whatwewilldoinsidenotes.jsisaddthedebuggerstatementin
logNotefunctionasthefirstlineofthefunction.ThenIwillruntheprogramin
debug mode, passing in some arguments that will cause logNote to run; for
example,readinganote,afterthenotegetsfetched,it'sgoingtocalllogNote.
Now, once we have the debugger keyword in the logNote function and run it in
debugmodewiththosearguments,theprogramshouldstopatthispoint.Once
theprogramstartsindebugmode,we'llusectocontinue,andit'llpause.Next,
we'llprintoutthenoteobjectandmakesureitlooksokay.Then,wecanquitrepl
andquitthedebugger.
Now,firstweareaddingthedebuggerstatementrighthere:
varlogNote=(note)=>{
debugger;
console.log('--');
console.log(`Title:${note.title}`);
console.log(`Body:${note.body}`);
};
Wecansavethefile,andnowwecanmoveintoTerminal;there'snoneedtodo
anythingelseinsideourapp.
InsideTerminalwe're goingto run ourapp.jsfile, node debug app.js, because we
want to run the program in debug mode. Then we can pass in our arguments,
let'ssaythereadcommand,andI'llpassinatitle,"tobuy"asshownhere:
nodedebugapp.jsread--title="tobuy"
InthiscaseIhaveanotewiththetitle"tobuy",asshownhere:
Now, when I run the preceding command, it's going to pause before that first
statementruns,thisisexpected:
I can now use c to continue through the program. It's going to run as many
statementsasittakesforeithertheprogramtoendorforthedebuggerkeywordto
befound,andasshowninthefollowingcode,youcanseethedebuggerwasfound
andourprogramhasstoppedonline49ofnotes.js:
Thisisexactlywhatwewantedtodo.Now,fromhere,I'llgointoreplandprint
outnoteargument,andasshowninthefollowingcode,youcanseewehavethe
notewiththetitleoftobuyandthebodyfood:
Now,iftherewasanerrorinthisstatement,maybethewrongthingwasprinting
tothe screen, this would give us a pretty good idea as to why. Whatever gets
passedintothenoteisclearlybeingusedinsideoftheconsole.logstatements,soif
therewasanissuewithwhat'sprinting,it'smostlikelyanissuewithwhatgets
passedintothelogNotefunction.
Nowthatwe'veprintedthenotevariable,wecanshutdownrepl,andwecanuse
control+Corquittoquitthedebugger.
Nowwe'rebackattheregularTerminalandwehavesuccessfullycompletedthe
debugginginsidetheNodeapplication.Inthenextsection,we'regoingtolookat
a different way to do the same thing, a way with a much nicer graphic user
interfacethatIfindaloteasiertonavigateanduse.
Listingnotes
Nowthatwe'vemadesomeawesomeprogressondebugging,let'sgobacktothe
commands for our app, because there is only one more to fill out (we have
coveredtheadd,read,andremovecommandsintheChapter3,NodeFundamentals-
Part2,andthischapter,respectively).It'sthelistcommand,andit'sgoingtobe
reallyeasy,thereisnothingcomplexgoingoninthecaseofthelistcommand.
UsingthegetAllfunction
Inordertogetstarted,allweneedtodoisfilloutthelistnotesfunction,which
inthiscasewecalledgetAll.ThegetAllfunctionisresponsibleforreturningevery
singlenote.Thatmeansit'sgoingtoreturnanarrayofobjects,anarrayofallof
ournotes.
AllwehavetodothatistoreturnfetchNotes,asshownhere:
vargetAll=()=>{
returnfetchNotes();
}
There'snoneedtofilter,there'snoneedtomanipulatethedata,wejustneedto
passthedatafromfetchNotesbackthroughgetAll.Nowthatwehavethisinplace,
wecanfilloutthefunctionalityoverinsideofapp.js.
Wehavetocreateavariablewherewecanstorethenotes,Iwasgoingtocallit
notes, but I probably shouldn't because we already have a notes variable
declared.I'llcreateanothervariable,calledallNotes,settingitequaltothereturn
valuefromgetAll,whichweknowbecausewejustfilledoutreturnsallthenotes:
elseif(command==='list'){
varallNotes=notes.getAll();
}
NowIcanuseconsole.logtoprintalittlemessageandI'llusetemplatestringssoI
caninjecttheactualnumberofnotesthataregoingtobeprinted.
Insidethetemplatestrings,I'lladdPrinting,thenthenumberofnotesusingthe$
(dollar) sign and the curly braces, allNotes.length: that's the length of the array
followed by notes with the s in parenthesis to handle both singular and plural
cases,asshowninthefollowingcodeblock:
elseif(command==='list'){
varallNotes=notes.getAll();
console.log(`Printing${allNotes.length}note(s).`);
}
So,ifthereweresixnotes,itwouldsayprintingsixnotes.
Now that we have this in place, we have to go about the process of actually
printingeachnote,which meansweneedtocalllogNoteoncefor everyitemin
theallNotesarray.Todo,thiswe'lluseforEach,whichisanarraymethodsimilarto
filter.
Filter lets you manipulate the array by returning true or false to keep items or
removeitems;forEachsimplycallsacallbackfunctiononceforeachiteminthe
array. In this case we can use it using allNotes.forEach, passing in a callback
function.Now,thatcallbackfunctionwillbeanarrowfunction(=>)inourcase,
andit willget calledwiththe notevariablejust like filter wouldhave.And all
we'llcallisnotes.logNote,passinginthenoteargument,whichisrighthere:
elseif(command==='list'){
varallNotes=notes.getAll();
console.log(`Printing${allNotes.length}note(s).`);
allNotes.forEach((note)=>{
notes.logNote(note);
});
}
Andnow thatwe havethisin place,we canactuallysimplifyitby addingthe
logNotecall,asshowninhere:
elseif(command==='list'){
varallNotes=notes.getAll();
console.log(`Printing${allNotes.length}note(s).`);
allNotes.forEach((note)=>notes.logNote(note));
}
Thisistheexactsamefunctionality,onlyusingtheexpressionsyntax.Nowthat
wehave our arrow function (=>) in place, we are calling notes.logNote once for
eachitemintheallnotesarray.Let'ssavetheapp.jsfileandtestthisoutoverin
Terminal.
Inordertotestoutthelistcommand,allI'lluseisnodeapp.jswiththecommand
list.Thereisnoneedtopassinanyarguments:
nodeapp.jslist
WhenIrunthis,IdogetPrinting3note(s)andthenIgetmy3notestobuy,tobuy
from store, and things to do, as shown in the following code output, which is
fantastic:
With this in place, all of our commands are now working. We can add notes,
remove notes, read an individual note, and list all of the notes stored in our
JSONfile.
Movingontothenextsection,Iwanttocleanupsomeofthecommands.Inside
app.jsandnotes.js,wehavesomeconsole.logstatementsthatareprintingoutafew
thingswenolongerneed.
Attheverytopofapp.js,Iam goingto removetheconsole.log('Starting app.js')
statement,makingtheconstantfsthefirstline.
I'll also remove the two statements: console.log('Command: ', command) and
console.log('Yargs',argv)thatprintthecommandandtheyargsvariablevalue.
Insidenotes.js, I will also remove the console.log('Stating notes.js') statement at
theverytopofthatfile,sinceitisnolongernecessary,puttingconstantfsatthe
top.
Itwasdefinitelyuseful whenwefirststarted exploringdifferentfiles,but now
wehaveeverythingin place,there'snoneed.IfIrerun thelistcommand,this
timeyoucanseeitlooksalotcleaner:
Printingthreenotesistheveryfirstlineshowingup.Withthisinplace,wehave
doneourcommands.
Inthenextsection,we'regoingtotakeaslightlymorein-depthlookathowwe
can configure yargs. This is going to let us require certain arguments for our
commands.So if someone tries to add a note without a title, we can warn the
userandpreventtheprogramfromexecuting.
Advancedyargs
Beforewegetintotheadvanceddiscussionofyargs,first,Iwanttopullupthe
yargs docs so that you at least know where the information about yargs is
comingfrom.YoucangetitbyGooglingnpmyargs.We'regoingtogototheyargs
packagepageonnpm.Thishasthedocumentationforyargs,asshownhere:
Now there is no table of contents for the yargs docs, which makes it kind of
difficult to navigate. It starts off with some examples that don't go in any
particularorder,andtheneventuallyitgetsintoalistofallthemethodsyouhave
available,andthat'swhatwe'relookingfor.
SoI'llusecommand+F(Ctrl+F)tosearchthepageformethods,andasshown
inthefollowingscreenshot,wegetthemethodsheader,whichistheonewe're
lookingfor:
If you scroll down on the page, we start to see an alphabetical list of all the
methods you have access to inside of yargs. We're specifically looking for
.command;thisisthemethodwecanusetoconfigureallfourofourcommands:the
add,read,removeandlistnotes:
We'regoingtospecifywhichoptionstheyrequire,ifany,andwecanalsosetup
thingslikedescriptionsandhelpfunctionality.
Usingchainingsyntaxonyargs
Now in order to get started, we need to make some changes inside of app.js.
We'regoingtostartwiththeaddcommand(formoreinformation,pleasereferto
theAddingandsavingnotessectioninthepreviouschapter).
Wewanttoaddafewhelpfulpiecesofinformationinargvfunctioninsideapp.js,
thatwill:
Letyargsverifytheaddcommandisranappropriately,and
Lettheuserknowhowtheaddcommandismeanttobeexecuted
Now we are going to be chaining property calls, which means right before I
access.argvIwanttocall.command,andthenI'llcall.argvonthereturnvaluefrom
commandasshownhere:
constargv=yargs
.command()
.argv;
Nowthischainingsyntaxprobablylooksfamiliarifyou'veusedjQuery,alotof
differentlibraries are supported. Once we call .command on yargs, we're going to
passinthreearguments.
Thefirstoneisthecommandname,exactlyhowtheuserisgoingtotypeitin
Terminal,inourcaseit'sgoingtobeadd:
constargv=yargs
.command('add')
.argv;
Thenwe'regoingtopassanotherstringin,andthisisgoingtobeadescription
of what the command does. It is going to be some sort of English readable
descriptionthat a user can read to figure out weather that's the command that
theywanttorun:
constargv=yargs
.command('add','Addanewnote')
.argv;
Thenextoneisgoingtobeanobject.Thisisgoingtobetheoptionsobjectthat
letsusspecifywhatargumentsthiscommandrequires.
Callingthe.helpcommand
Now before we get into the options object, let's add one more call right after
command.We'regoingtocall.help,whichisamethod,sowe'regoingtocallit
asafunction,andwedon'tneedtopassinanyarguments:
constargv=yargs
.command('add','Addanewnote',{
})
.help()
.argv;
When we add on this help call, it sets up yargs to return some really useful
information when someone runs the program. For example, I can run the node
app.jscommandwiththehelpflag.Thehelpflagisaddedbecausewecalledthat
helpmethod,andwhenIruntheprogram,youcanseealloftheoptionswehave
available:
nodeapp.js--help
Asshownintheprecedingoutput,wehaveonecommand,addAddanewnote,and
ahelpoptionforthecurrentcommand,help.Andthesamethingholdstrueifwe
runthenodeapp.jsaddcommandwithhelpasshownhere:
nodeapp.jsadd--help
Inthisoutput,we canviewalloftheoptionsand argumentsforaddcommand,
whichinthiscasehappenstobenonebecausewehaven'tsetthoseup:
Addingtheoptionsobject
Let's add options and argumentsback inside Atom. In order to add properties,
we'regoing to update the options object, where the key is the property name,
whetherit'stitleorbody,andthevalueisanotherobjectthatletsusspecifyhow
thatpropertyshouldwork,asshownhere:
constargv=yargs
.command('add','Addanewnote',{
title:{

}
})
.help()
.argv;
Addingthetitle
Inthecaseoftitle,wewouldaddthetitleontheleft-handside,andwewould
put our options object on the right-hand side. Inside the title, we're going to
configurethreepropertiesdescribe,demand,andalias:
Thedescribepropertywill besetequalto astring,andthisisgoingto describe
whatissupposedtobepassedinforthetitle.Inthiscase,wecanjustuseTitleof
note:
constargv=yargs
.command('add','Addanewnote',{
title:{
describe:'Titleofnote'
}
})
.help()
.argv;
Nextweconfiguredemand.Itisgoingtotellyargwhetherornotthisargumentis
required.demandisfalsebydefault,we'llsetittotrue:
constargv=yargs
.command('add','Addanewnote',{
title:{
describe:'Titleofnote',
demand:true
}
})
.help()
.argv;
Nowifsomeonetriestoruntheaddcommandwithoutthetitle,it's
going to fail, and we can prove this. We can save app.js, and in
Terminal, we can rerun our previous command removing the help
flag,andwhenIdothat,youseewegetawarning,Missingrequired
argument:titleasshownhere:
Noticethatintheoutputthetitleargument,isTitleofnote,whichis
the describe string we used, and it's required on the right side,
letting you know that you have to provide a title when you're
callingthataddcommand.
Along with describe and demand we are going to provide a third option, this is
calledalias. The alias lets you provide a shortcut so you don't have to type --
title;youcansetthealiasequaltoasinglecharacterliket:
constargv=yargs
.command('add','Addanewnote',{
title:{
describe:'Titleofnote',
demand:true,
alias:'t'
}
})
.help()
.argv;
Whenyouhavedonethat,youcannowrunthecommandinTerminalusingthe
newsyntax.
Let'srunouraddcommand,nodeapp.jsadd,insteadof--title.We'regoingtouse
-t,whichistheflagversion,andwecansetthatequaltowhateverwelike,for
example,flagtitlewillbethetitle,and--bodywillgetsetequaltobody,asshown
inthefollowingcode.Notethatwehaven'tsetupthebodyargumentyetsothere
isnoalias:
nodeapp.jsadd-t="flagtitle"--body="body"
If I run this command, everything works as expected. The flag title shows up
rightwhereitshould,eventhoughweusedthealiasversionwhichisthelettert,
asshownhere:
Addingthebody
Nowthatwehaveourtitleconfigured,wecandotheexactsamethingforthe
body. We'll specify our options object and provide those three arguments:
describe,demand,andaliasforbody:
constargv=yargs
.command('add','Addanewnote',{
title:{
describe:'Titleofnote',
demand:true,
alias:'t'
},
body:{

}
})
.help()
.argv;
Thefirstoneisdescribeandthatone'sprettyeasy.describeisgoingtogetsetequal
toastring,andinthiscaseBodyofnotewillgetthejobdone:
constargv=yargs
.command('add','Addanewnote',{
title:{
describe:'Titleofnote',
demand:true,
alias:'t'
},
body:{
describe:'Bodyofnote'
}
})
.help()
.argv;
Thenext one willbedemand, and to add anotewe are going to need abody.So
we'llsetdemandequaltotrue,justlikewedouppreviousfortitle:
constargv=yargs
.command('add','Addanewnote',{
title:{
describe:'Titleofnote',
demand:true,
alias:'t'
},
body:{
describe:'Bodyofnote'
demand:true
}
})
.help()
.argv;
Andlastbutnotleastisthealias.Thealiasisgoingtogetsetequaltoasingle
letter,I'llusetheletterbforbody:
constargv=yargs
.command('add','Addanewnote',{
title:{
describe:'Titleofnote',
demand:true,
alias:'t'
},
body:{
describe:'Bodyofnote'
demand:true,
alias:'b'
}
})
.help()
.argv;
Withthisinplace, wecan nowsave app.js and inside Terminal, we can take a
momenttorerunnodeapp.jsaddwiththehelpflag:
nodeapp.jsadd--help
Whenwerunthiscommand,weshouldnowseethebodyargumentshowingup,
andyoucanevenseeitshowstheflagversion,asshowninthefollowingoutput,
thealias-b(Bodyofnote),anditisrequired:
NowI'llrunnodeapp.jsaddpassingintwoargumentst.I'llsetthatequaltot,and
bsettingitequaltob.
WhenIrunthecommand,everythingworksasexpected:
nodeapp.jsadd-t=t-b=b
Asshownintheprecedingoutputscreenshot,anewnotewascreatedwithatitle
oftandabodyofb.Withthisinplace,we'venowsuccessfullycompletedthe
setupfortheaddcommand.Wehaveouraddcommandtitle,adescription,andthe
block that specifies the arguments for that command. Now we do have three
morecommandstoaddsupportfor,solet'sgetstarteddoingthat.
Adding support to the read and
removecommands
Onthenextline,I'llcall.commandagain,passinginthecommandname.Let'sdo
thelistcommandfirstbecausethisoneisreallyeasy,noargumentsarerequired.
Thenwe'llpassinthedescriptionforthelistcommand,Listallnotes,asshown
here:
.command('list','Listallnotes')
.help()
.argv;
Nextup,we'llcallcommandagain.Thistimewe'lldothecommandforread.The
read command reads an individual note, so for the description for the read
command,we'llusesomethinglikeReadanote:
.command('list','Listallnotes')
.command('read','Readanote')
.help()
.argv;
Nowthereadcommanddoesrequirethetitleargument.Thatmeanswearegoing
toneedtoprovidethatoptionsobject.I'lltaketitlefromaddcommand,copyit,
andpasteitinthereadcommandoptionsobject:
.command('list','Listallnotes')
.command('read','Readanote',{
title:{
describe:'Titleofnote',
demand:true,
alias:'t'
}
})
.help()
.argv;
Asyouprobablyjustnoticed,wehaverepeatedcode.Thetitleconfigurationjust
got copied and pasted into multiple places. It would be pretty nice if this was
DRY,ifit wasin onevariablewecouldreferenceinboth locations,in addand
readcommands.
Willcallcommandforremove,justfollowingwherewecalledthecommandfor
read. Now, the remove command will have a description. We'll stick with
somethingsimplelikeRemoveanote,andwewillbeprovidinganoptionsobject:
.command('remove','Removeanote',{
})
NowIcanaddtheoptionsobjectidenticaltothereadcommand.However,inthat
options object, I'll set title equal to titleOptions, as shown here, to avoid the
repetitionofcode:
.command('remove','Removeanote',{
title:titleOptions
})
Adding the titleOption and
bodyOptionvariables
NowIdon'thavethetitleOptionsobjectcreatedyetsothecodewouldcurrently
fail,butthisis the general idea. We want to create the titleOptions object once
andreferenceitinallthelocationsweuseit,foradd,readandremovecommand.I
cantaketitleOptions, and add it for read as well as for add command, as shown
here:
.command('add','Addanewnote',{
title:titleOptions,
body:{
describe:'Bodyofnote',
demand:true,
alias:'b'
}
})
.command('list','Listallnotes')
.command('read','Readanote',{
title:titleOptions
})
.command('remove','Removeanote',{
title:titleOptions
})
Now, just previousthe constant argv, I can create a constant called titleOptions,
and I can set it equal to that object that we defined for add and read command
earlier,whichisdescribe,demand,andalias,asshownhere:
consttitleOptions={
describe:'Titleofnote',
demand:true,
alias:'t'
};
WenowhavethetitleOptionsinplace,andthiswillworkasexpected.Wehave
theexactsamefunctionalitywedidbefore,butwenowhavethetitleOptionsina
separateobject, whichfollowsthe DRY principlewe discussed intheReading
notesection.
Now,wecouldalsodothesamethingforbody.Itmightseemlikeoverkillsince
we'reonly using it in only one location, but if we're sticking to the pattern of
breakingthemout intovariables,I'lldoitinthecase ofthebodyaswell.Just
followingthetitleOptionsconstant,IcancreatetheconstantbodyOptions,settingit
equal to the options object we defined in the body, for add command in the
previoussubsection:
constbodyOptions={
describe:'Bodyofnote',
demand:true,
alias:'b'
};
Withthisinplace,wearenowdone.Wehaveadd,read,andremove,allwiththeir
argumentssetupreferencingthetitleObjectandbodyObjectvariablesdefined.
Testingtheremovecommand
Let's test out the remove command in Terminal. I'll list out my notes using node
app.jslist,soIcanseewhichnotesIhavetoremove:
nodeapp.jslist
I'llremovethenotewiththetitlet,usingthenode app.js removecommandandour
flag"t":
nodeapp.jsremove-t="t"
We'll remove the note with the title t, and as shown previous, Note was removed
printstothescreen.AndifIusetheuparrowkeytwice,Icanlistthenotesout
again,andyoucanseethenotewiththetitlethasindeedgone:
Let's remove one more note using the node app.js remove command. This time
we'regoingtouse--title,whichistheargumentname,andthenotewe'regoing
toremovehasthetitleflagtitle,asshowninthiscode:
WhenIremoveit,itsaysNotewasremoved,andifIrerunthelistcommand,Ican
seethatwehavethreenotesleft,thenotewasindeedremoved,asshownhere:
Andthatisitforthenotesapplication.
Arrowfunctions
Inthissection,you'regoingtolearntheinsandoutsofthearrowfunction.It'san
ES6feature,andwehavetakenalittlelookatit.Insidenotes.jsweuseditina
few basic examples to create methods such as fetchNotes and saveNotes, and we
alsopasseditintoafewarraymethodslikefilter,andforeacharray,weusedit
asthecallbackfunctionthatgetscalledonceforeveryiteminthearray.
Now if you try to swap out all of the functions in a program with arrow
functions,it'smostlikelynotgoingtoworkasexpectedbecausetherearesome
differences between the two, and it's really important to know what those
differencesare,soyoucanmakethedecisiontousearegularES5functionoran
ES6arrowfunction.
Usingthearrowfunction
Thegoalinthissectionistogiveyoutheknowledgetomakethatchoice,and
we'llkickthingsoffbycreatinganewfileintheplaygroundfoldercalledarrow-
function.js:
Inside this file, we're going to play around with a few examples, going over
someofthesubtletiestothearrowfunction.Beforewetypeanythinginsideof
the file, I'll start up this file with nodemon, so every time we make a change it
automaticallyrefreshesoverinTerminal.
If you remember, nodemon is the utility we installed in Chapter 2, Node
Fundamentals-Part1.Itwasaglobalnpmmodule.Thenodemonisthecommand
torun,andthenwejustpassinthefilepathlikewewouldforanyotherNode
command. As we're going into the playgroundfolder, and the file itself is called
arrow-function.js,we'llrunthefollowingcommand:
nodemonplayground/arrow-function.js
We'll run the file, and nothing prints to the screen, as shown in the following
output,besidesthenodemonlogsbecausewehavenothinginthefile:
Togetstarted,inthearrowfunction.jsfile,we'llcreateafunctioncalledsquare,by
makingavariablecalledsquareandsettingitequaltoanarrowfunction.
To make our arrow function (=>), we'll first provide the arguments inside
parentheses.Sincewe'llbesquaringanumber,wejustneedonenumber,andI'll
refertothatnumberasx.IfIpassin3,Ishouldexpect9back,andifIpassin9,
Iwouldexpect81back.
After the arguments list, we have to put the arrow in arrow function (=>) by
putting the equal sign and the greater than symbol together, creating our nice
littlearrow.Fromherewecanprovide,insidecurlybraces,allthestatementswe
wanttoexecute:
varsquare=(x)=>{
};
Next,wemightcreateavariablecalledresult,settingthatequaltoxtimesx,then
wemightreturntheresultvariableusingthereturnkeyword,asshownhere:
varsquare=(x)=>{
varresult=x*x;
returnresult;
};
Now,obviouslythiscanbedoneononeline,butthegoalhereistoillustratethat
when you use the statement arrow function (=>), you can put as many lines as
youwantinbetweenthosecurlybraces.Let'scallasquare,we'lldothatusing
console.logsowecanprinttheresulttothescreen.I'llcallsquare;andwe'llcall
squarewith9,thesquareof9wouldbe81,sowewouldexpect81toprinttothe
screen:
varsquare=(x)=>{
varresult=x*x;
returnresult;
};
console.log(square(9));
I'll save the arrow function (=>) file, and in Terminal, 81 shows up just as we
expect:
Nowthesyntaxweusedinthepreviousexampleisthestatementsyntaxforthe
arrowfunction(=>).We'vealsoexploredtheexpressionsyntaxearlier,whichlets
you simplify your arrow functions when you return some expressions. In this
caseallweneedtodoisspecifytheexpressionwewanttoreturn.Inourcase
that'sxtimesx:
varsquare=(x)=>x*x;
console.log(square(9));
You don't need to explicitly add the return keyword. When you use an arrow
function (=>) without your curly braces, it's implicitly provided for you. That
meanswecansavethefunctionasshownpreviousandtheexactsameresultis
goingtoprinttothescreen,81showsup.
This is one of the great advantages of arrow functions when you use them in
caseslikefilterorforthosewhichwedidinthenotes.jsfile.Itletsyousimplify
yourcodekeepingeverythingononelineandmakingyourcodealoteasierto
maintainandscan.
Now, there is one thing I want to note: when you have an arrow
function(=>)thathasjustoneargument,youcanactuallyleaveoff
theparentheses. If youhavetwo or morearguments, or youhave
zeroarguments,youaregoingtoneedtoprovidetheparentheses,
but if you just have one argument, you can reference it with no
parentheses.
IfIsavethefileinthisstate,81stillprintstothescreen;andthisisgreatwehave
anevensimplerversionofourarrowfunction(=>):
Nowthatwehaveabasicexampledown,Iwanttomoveontoamorecomplex
examplethat'sgoingtoexplorethenuancesbetweenregularfunctionsandarrow
functions.
Exploring the difference between
regularandarrowfunctions
To illustrate the difference, I'll make a variable called user, which will be an
object. On this object we'll specify one property, name. Set name equal to the
string,yourname,inthiscaseI'llsetitequaltothestringAndrew:
varuser={
name:'Andrew'
};
Then we can define a method on the user object. Right after name, with my
commaattheendoftheline,I'llprovidethemethodsayHi,settingitequaltoan
arrowfunction(=>)thatdoesn'ttakeanyarguments.Forthemoment,we'llkeep
thearrowfunctionreallysimple:
varuser={
name:'Andrew',
sayHi:()=>{
}
};
Allwe'lldoinsidesayHiisuseconsole.logtoprinttothescreen,insideoftemplate
stringsHi:
varuser={
name:'Andrew',
sayHi:()=>{
console.log(`Hi`);
}
};
We're not using template strings yet, but we will later so I'll use them here.
Downfollowingtheuserobject,wecantestoutsayHibycallingit,user.sayHi:
varuser={
name:'Andrew',
sayHi:()=>{
console.log(`Hi`);
}
};
user.sayHi();
I'll call it then save the file, and we would expect that Hi prints to the screen
becauseallourarrowfunction(=>)doesisuseconsole.logtoprintastaticstring.
Nothinginthiscasewillcauseanyproblems;you'dbeabletoswapoutaregular
functionforanarrowfunction(=>)withoutissue.
Nowthefirstissuethatwillarisewhenyouusearrowfunctionsisthefactthat
arrowfunctionsdonotbindathiskeyword.Soifyouareusingthisinsideyour
function,it'snotgoingtoworkwhenyouswapitoutforanarrowfunction(=>).
Now,this binding; refers to the parent binding, in our case there is no parent,
function so this would refer to the global this keyword. Now we have our
console.logthatdoesnotusethis,I'llswapitoutforacasethatdoes.
We'll put a period after Hi, and I'll say I'm, followed by the name, which we
wouldusuallybeabletoaccessviathis.name:
varuser={
name:'Andrew',
sayHi:()=>{
console.log(`Hi.I'm${this.name}`);
}
};
user.sayHi();
IfItrytorunthiscode,itisnotgoingtoworkasexpected;we'regoingtogetHi
I'mundefinedprintingtothescreen,asshownhere:
Inordertofixthis,we'lllookatanalternativesyntaxtoarrowfunctionsthat's
greatwhenyou'redefiningobjectliterals,asweareinthiscase.
AftersayHi,I'llmakeanewmethodcalledsayHiAltusingadifferentES6feature.
ES6providesusanewwaytomakemethodsonobjects;youprovidethemethod
name,sayHiAlt,thenyou gorighttotheparenthesesskippingthecolon.There's
alsononeedforthefunctionkeyword,eventhoughitis aregularfunctionit's
notanarrowfunction(=>).Thenwemoveontoourcurlybracesasshownhere:
varuser={
name:'Andrew',
sayHi:()=>{
console.log(`Hi.I'm${this.name}`);
},
sayHiAlt(){

}
};
user.sayHi();
InsidehereIcanhavetheexactsamecodewehaveinthesayHifunction,butitis
goingtoworkasexpected.It'sgoingtoprintHi.I'mAndrew.I'llcallsayHiAltdown
followinginsteadoftheregularsayHimethod:
varuser={
name:'Andrew',
sayHi:()=>{
console.log(`Hi.I'm${this.name}`);
},
sayHiAlt(){
console.log(`Hi.I'm${this.name}`);
}
};
user.sayHiAlt();
AndinTerminal,youcanseeHi.I'mAndrew,printstothescreen:
ThesayHiAltsyntaxisasyntaxthatyoucanusetosolvethisproblemwhenyou
createfunctionsonobjectliterals.Nowthatweknowthatthethiskeyworddoes
not get bound, let's explore one other quirk that arrow functions have, it also
doesnotbindtheargumentsarray.
Exploringtheargumentsarray
Regular functions, like sayHiAlt, are going to have an arguments array that's
accessibleinsideofthefunction:
varuser={
name:'Andrew',
sayHi:()=>{
console.log(`Hi.I'm${this.name}`);
},
sayHiAlt(){
console.log(arguments);
console.log(`Hi.I'm${this.name}`);
}
};
user.sayHiAlt();
Now,it'snotanactualarray,it'smorelikeanobjectwitharray;likeproperties,
but the argumentsobject is indeed specified in a regular function. If I pass in
one, two, and three and save the file, we'll get that back when we log out
arguments:
varuser={
name:'Andrew',
sayHi:()=>{
console.log(`Hi.I'm${this.name}`);
},
sayHiAlt(){
console.log(arguments);
console.log(`Hi.I'm${this.name}`);
}
};
user.sayHiAlt(1,2,3);
Insidenodemon, it's taking a quick second to restart, and right here we have our
object:
Wehaveone,two,andthree,wehavetheindexforeachasthepropertyname,
andthisworksbecausewe'reusingaregularfunction.Ifwe weretoswitchto
thearrowfunction(=>)though,itisnotgoingtoworkasexpected.
I'lladdconsole.log(arguments)insideofmyarrowfunction(=>),andI'llswitchfrom
callingsayHiAltbacktotheoriginalmethodsayHi,asshownhere:
varuser={
name:'Andrew',
sayHi:()=>{
console.log(arguments);
console.log(`Hi.I'm${this.name}`);
},
sayHiAlt(){
console.log(arguments);
console.log(`Hi.I'm${this.name}`);
}
};
user.sayHi(1,2,3);
WhenIsavethefileinarrow-function.js,we'llgetsomethingalotdifferentfrom
what we had before. What we'll actually get is the global arguments variable,
whichistheargumentsvariableforthatwrapperfunctionweexplored: